You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2018/03/27 11:42:46 UTC

[cloudstack] 01/01: [Merge 4.11] CLOUDSTACK-10232: SystemVMs and VR to run as HVM on XenServer (#2465)

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

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

commit 8f881ce08be7e88b200ac9692c840ed38c5ee9f2
Merge: 0afcec6 535e615
Author: Rohit Yadav <ro...@shapeblue.com>
AuthorDate: Tue Mar 27 17:12:14 2018 +0530

    [Merge 4.11] CLOUDSTACK-10232: SystemVMs and VR to run as HVM on XenServer (#2465)
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>

 .../xenserver/resource/CitrixResourceBase.java     | 29 ++++++++++++++--------
 .../debian/opt/cloud/bin/setup/cloud-early-config  | 18 ++++++++++++--
 2 files changed, 34 insertions(+), 13 deletions(-)

diff --cc plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
index ee146a1,0000000..88df2ce
mode 100644,000000..100644
--- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
@@@ -1,5612 -1,0 +1,5619 @@@
 +// 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.hypervisor.xenserver.resource;
 +
 +import java.io.BufferedReader;
 +import java.io.BufferedWriter;
 +import java.io.File;
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +import java.io.InputStreamReader;
 +import java.io.OutputStreamWriter;
 +import java.net.MalformedURLException;
 +import java.net.URI;
 +import java.net.URISyntaxException;
 +import java.net.URL;
 +import java.net.URLConnection;
 +import java.nio.charset.Charset;
 +import java.util.ArrayList;
 +import java.util.Date;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.Iterator;
 +import java.util.LinkedList;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Objects;
 +import java.util.Properties;
 +import java.util.Queue;
 +import java.util.Random;
 +import java.util.Set;
 +import java.util.UUID;
 +import java.util.concurrent.TimeoutException;
 +
 +import javax.naming.ConfigurationException;
 +import javax.xml.parsers.DocumentBuilderFactory;
 +import javax.xml.parsers.ParserConfigurationException;
 +
 +import org.apache.cloudstack.storage.to.TemplateObjectTO;
 +import org.apache.cloudstack.storage.to.VolumeObjectTO;
 +import org.apache.commons.collections.MapUtils;
 +import org.apache.commons.io.FileUtils;
 +import org.apache.log4j.Logger;
 +import org.apache.xmlrpc.XmlRpcException;
 +import org.joda.time.Duration;
 +import org.w3c.dom.Document;
 +import org.w3c.dom.Node;
 +import org.w3c.dom.NodeList;
 +import org.xml.sax.InputSource;
 +import org.xml.sax.SAXException;
 +
 +import com.cloud.agent.IAgentControl;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.GetHostStatsCommand;
 +import com.cloud.agent.api.GetVmStatsCommand;
 +import com.cloud.agent.api.HostStatsEntry;
 +import com.cloud.agent.api.HostVmStateReportEntry;
 +import com.cloud.agent.api.PingCommand;
 +import com.cloud.agent.api.PingRoutingCommand;
 +import com.cloud.agent.api.PingRoutingWithNwGroupsCommand;
 +import com.cloud.agent.api.PingRoutingWithOvsCommand;
 +import com.cloud.agent.api.RebootAnswer;
 +import com.cloud.agent.api.RebootCommand;
 +import com.cloud.agent.api.SetupGuestNetworkCommand;
 +import com.cloud.agent.api.StartAnswer;
 +import com.cloud.agent.api.StartCommand;
 +import com.cloud.agent.api.StartupCommand;
 +import com.cloud.agent.api.StartupRoutingCommand;
 +import com.cloud.agent.api.StartupStorageCommand;
 +import com.cloud.agent.api.StopAnswer;
 +import com.cloud.agent.api.StopCommand;
 +import com.cloud.agent.api.StoragePoolInfo;
 +import com.cloud.agent.api.VgpuTypesInfo;
 +import com.cloud.agent.api.VmStatsEntry;
 +import com.cloud.agent.api.routing.IpAssocCommand;
 +import com.cloud.agent.api.routing.IpAssocVpcCommand;
 +import com.cloud.agent.api.routing.NetworkElementCommand;
 +import com.cloud.agent.api.routing.SetNetworkACLCommand;
 +import com.cloud.agent.api.routing.SetSourceNatCommand;
 +import com.cloud.agent.api.to.DataStoreTO;
 +import com.cloud.agent.api.to.DataTO;
 +import com.cloud.agent.api.to.DiskTO;
 +import com.cloud.agent.api.to.GPUDeviceTO;
 +import com.cloud.agent.api.to.IpAddressTO;
 +import com.cloud.agent.api.to.NfsTO;
 +import com.cloud.agent.api.to.NicTO;
 +import com.cloud.agent.api.to.VirtualMachineTO;
 +import com.cloud.agent.resource.virtualnetwork.VRScripts;
 +import com.cloud.agent.resource.virtualnetwork.VirtualRouterDeployer;
 +import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
 +import com.cloud.exception.InternalErrorException;
 +import com.cloud.host.Host.Type;
 +import com.cloud.hypervisor.Hypervisor.HypervisorType;
 +import com.cloud.hypervisor.xenserver.resource.wrapper.xenbase.CitrixRequestWrapper;
 +import com.cloud.hypervisor.xenserver.resource.wrapper.xenbase.XenServerUtilitiesHelper;
 +import com.cloud.network.Networks;
 +import com.cloud.network.Networks.BroadcastDomainType;
 +import com.cloud.network.Networks.TrafficType;
 +import com.cloud.resource.ServerResource;
 +import com.cloud.resource.hypervisor.HypervisorResource;
 +import com.cloud.storage.Storage;
 +import com.cloud.storage.Storage.StoragePoolType;
 +import com.cloud.storage.Volume;
 +import com.cloud.storage.VolumeVO;
 +import com.cloud.storage.resource.StorageSubsystemCommandHandler;
 +import com.cloud.storage.resource.StorageSubsystemCommandHandlerBase;
 +import com.cloud.template.VirtualMachineTemplate.BootloaderType;
 +import com.cloud.utils.ExecutionResult;
 +import com.cloud.utils.NumbersUtil;
 +import com.cloud.utils.Pair;
 +import com.cloud.utils.PropertiesUtil;
 +import com.cloud.utils.StringUtils;
 +import com.cloud.utils.Ternary;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.utils.script.Script;
 +import com.cloud.utils.ssh.SSHCmdHelper;
 +import com.cloud.utils.ssh.SshHelper;
 +import com.cloud.vm.VirtualMachine;
 +import com.cloud.vm.VirtualMachine.PowerState;
 +import com.trilead.ssh2.SCPClient;
 +import com.xensource.xenapi.Bond;
 +import com.xensource.xenapi.Connection;
 +import com.xensource.xenapi.Console;
 +import com.xensource.xenapi.Host;
 +import com.xensource.xenapi.HostCpu;
 +import com.xensource.xenapi.HostMetrics;
 +import com.xensource.xenapi.Network;
 +import com.xensource.xenapi.PBD;
 +import com.xensource.xenapi.PIF;
 +import com.xensource.xenapi.Pool;
 +import com.xensource.xenapi.SR;
 +import com.xensource.xenapi.Session;
 +import com.xensource.xenapi.Task;
 +import com.xensource.xenapi.Types;
 +import com.xensource.xenapi.Types.BadServerResponse;
 +import com.xensource.xenapi.Types.VmPowerState;
 +import com.xensource.xenapi.Types.XenAPIException;
 +import com.xensource.xenapi.VBD;
 +import com.xensource.xenapi.VDI;
 +import com.xensource.xenapi.VIF;
 +import com.xensource.xenapi.VLAN;
 +import com.xensource.xenapi.VM;
 +import com.xensource.xenapi.XenAPIObject;
 +
 +/**
 + * CitrixResourceBase encapsulates the calls to the XenServer Xapi process to
 + * perform the required functionalities for CloudStack.
 + *
 + * ==============> READ THIS <============== Because the XenServer objects can
 + * expire when the session expires, we cannot keep any of the actual XenServer
 + * objects in this class. The only thing that is constant is the UUID of the
 + * XenServer objects but not the objects themselves! This is very important
 + * before you do any changes in this code here.
 + *
 + */
 +public abstract class CitrixResourceBase implements ServerResource, HypervisorResource, VirtualRouterDeployer {
 +    /**
 +     * used to describe what type of resource a storage device is of
 +     */
 +    public enum SRType {
 +        EXT, FILE, ISCSI, ISO, LVM, LVMOHBA, LVMOISCSI,
 +        /**
 +         * used for resigning metadata (like SR UUID and VDI UUID when a
 +         * particular storage manager is installed on a XenServer host (for back-end snapshots to work))
 +         */
 +        RELVMOISCSI, NFS;
 +
 +        String _str;
 +
 +        private SRType() {
 +            _str = super.toString().toLowerCase();
 +        }
 +
 +        public boolean equals(final String type) {
 +            return _str.equalsIgnoreCase(type);
 +        }
 +
 +        @Override
 +        public String toString() {
 +            return _str;
 +        }
 +    }
 +
 +    private final static int BASE_TO_CONVERT_BYTES_INTO_KILOBYTES = 1024;
 +
 +    protected static final XenServerConnectionPool ConnPool = XenServerConnectionPool.getInstance();
 +    // static min values for guests on xenserver
 +    private static final long mem_128m = 134217728L;
 +
 +    static final Random Rand = new Random(System.currentTimeMillis());
 +    private static final Logger s_logger = Logger.getLogger(CitrixResourceBase.class);
 +    protected static final HashMap<VmPowerState, PowerState> s_powerStatesTable;
 +
 +    private String xenServer70plusGuestToolsName = "guest-tools.iso";
 +    private String xenServerBefore70GuestToolsName = "xs-tools.iso";
 +
 +    static {
 +        s_powerStatesTable = new HashMap<VmPowerState, PowerState>();
 +        s_powerStatesTable.put(VmPowerState.HALTED, PowerState.PowerOff);
 +        s_powerStatesTable.put(VmPowerState.PAUSED, PowerState.PowerOff);
 +        s_powerStatesTable.put(VmPowerState.RUNNING, PowerState.PowerOn);
 +        s_powerStatesTable.put(VmPowerState.SUSPENDED, PowerState.PowerOff);
 +        s_powerStatesTable.put(VmPowerState.UNRECOGNIZED, PowerState.PowerUnknown);
 +    }
 +
 +    private static PowerState convertToPowerState(final VmPowerState ps) {
 +        final PowerState powerState = s_powerStatesTable.get(ps);
 +        return powerState == null ? PowerState.PowerUnknown : powerState;
 +    }
 +
 +    private static boolean isAlienVm(final VM vm, final Connection conn) throws XenAPIException, XmlRpcException {
 +        // TODO : we need a better way to tell whether or not the VM belongs to
 +        // CloudStack
 +        final String vmName = vm.getNameLabel(conn);
 +        if (vmName.matches("^[ivs]-\\d+-.+")) {
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    protected IAgentControl _agentControl;
 +    protected boolean _canBridgeFirewall = false;
 +    protected String _cluster;
 +    // Guest and Host Performance Statistics
 +    protected String _consolidationFunction = "AVERAGE";
 +    protected long _dcId;
 +    protected String _guestNetworkName;
 +    protected int _heartbeatInterval = 60;
 +    protected int _heartbeatTimeout = 120;
 +    protected final XsHost _host = new XsHost();
 +    protected String _instance; // instance name (default is usually "VM")
 +    protected boolean _isOvs = false;
 +    protected String _linkLocalPrivateNetworkName;
 +    protected int _maxNics = 7;
 +
 +    final int _maxWeight = 256;
 +    protected int _migratewait;
 +    protected String _name;
 +    protected Queue<String> _password = new LinkedList<String>();
 +
 +    protected String _pod;
 +    protected int _pollingIntervalInSeconds = 60;
 +
 +    protected String _privateNetworkName;
 +    protected String _publicNetworkName;
 +
 +    protected final int _retry = 100;
 +
 +    protected boolean _securityGroupEnabled;
 +    protected final int _sleep = 10000;
 +    protected String _storageNetworkName1;
 +    protected String _storageNetworkName2;
 +    protected List<VIF> _tmpDom0Vif = new ArrayList<VIF>();
 +
 +    protected String _username;
 +
 +    protected VirtualRoutingResource _vrResource;
 +
 +    protected String _configDriveIsopath = "/opt/xensource/packages/configdrive_iso/";
 +    protected String _configDriveSRName = "ConfigDriveISOs";
 +    public String _attachIsoDeviceNum = "3";
 +
 +    protected XenServerUtilitiesHelper xenServerUtilitiesHelper = new XenServerUtilitiesHelper();
 +
 +    protected int _wait;
 +    // Hypervisor specific params with generic value, may need to be overridden
 +    // for specific versions
 +    long _xsMemoryUsed = 128 * 1024 * 1024L; // xenserver hypervisor used 128 M
 +
 +    double _xsVirtualizationFactor = 63.0 / 64.0; // 1 - virtualization overhead
 +
 +    protected StorageSubsystemCommandHandler storageHandler;
 +
++    private static final String XENSTORE_DATA_IP = "vm-data/ip";
++    private static final String XENSTORE_DATA_GATEWAY = "vm-data/gateway";
++    private static final String XENSTORE_DATA_NETMASK = "vm-data/netmask";
++    private static final String XENSTORE_DATA_CS_INIT = "vm-data/cloudstack/init";
++
 +    public CitrixResourceBase() {
 +    }
 +
 +    /**
 +     * Replaces the old password with the new password used to connect to the host.
 +     *
 +     * @param password  - the new host password.
 +     * @return the old password.
 +     */
 +    public String replaceOldPasswdInQueue(final String password) {
 +        final String oldPasswd = _password.poll();
 +        _password.add(password);
 +
 +        return oldPasswd;
 +    }
 +
 +    public String getPwdFromQueue() {
 +        return _password.peek();
 +    }
 +
 +    public XenServerUtilitiesHelper getXenServerUtilitiesHelper() {
 +        return xenServerUtilitiesHelper;
 +    }
 +
 +    protected StorageSubsystemCommandHandler buildStorageHandler() {
 +        final XenServerStorageProcessor processor = new XenServerStorageProcessor(this);
 +        return new StorageSubsystemCommandHandlerBase(processor);
 +    }
 +
 +    public String callHostPlugin(final Connection conn, final String plugin, final String cmd, final String... params) {
 +        final Map<String, String> args = new HashMap<String, String>();
 +        String msg;
 +        try {
 +            for (int i = 0; i < params.length; i += 2) {
 +                args.put(params[i], params[i + 1]);
 +            }
 +
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin executing for command " + cmd + " with " + getArgsString(args));
 +            }
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            final String result = host.callPlugin(conn, plugin, cmd, args);
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin Result: " + result);
 +            }
 +            return result.replace("\n", "");
 +        } catch (final XenAPIException e) {
 +            msg = "callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.toString();
 +            s_logger.warn(msg);
 +        } catch (final XmlRpcException e) {
 +            msg = "callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.getMessage();
 +            s_logger.debug(msg);
 +        }
 +        throw new CloudRuntimeException(msg);
 +    }
 +
 +    protected String callHostPluginAsync(final Connection conn, final String plugin, final String cmd, final int wait, final Map<String, String> params) {
 +        final int timeout = wait * 1000;
 +        final Map<String, String> args = new HashMap<String, String>();
 +        Task task = null;
 +        try {
 +            for (final Map.Entry<String, String> entry : params.entrySet()) {
 +                args.put(entry.getKey(), entry.getValue());
 +            }
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin executing for command " + cmd + " with " + getArgsString(args));
 +            }
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            task = host.callPluginAsync(conn, plugin, cmd, args);
 +            // poll every 1 seconds
 +            waitForTask(conn, task, 1000, timeout);
 +            checkForSuccess(conn, task);
 +            final String result = task.getResult(conn);
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin Result: " + result);
 +            }
 +            return result.replace("<value>", "").replace("</value>", "").replace("\n", "");
 +        } catch (final Types.HandleInvalid e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to HandleInvalid clazz:" + e.clazz + ", handle:" + e.handle);
 +        } catch (final Exception e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.toString(), e);
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e1) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e1.toString());
 +                }
 +            }
 +        }
 +        return null;
 +    }
 +
 +    protected String callHostPluginAsync(final Connection conn, final String plugin, final String cmd, final int wait, final String... params) {
 +        final int timeout = wait * 1000;
 +        final Map<String, String> args = new HashMap<String, String>();
 +        Task task = null;
 +        try {
 +            for (int i = 0; i < params.length; i += 2) {
 +                args.put(params[i], params[i + 1]);
 +            }
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin executing for command " + cmd + " with " + getArgsString(args));
 +            }
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            task = host.callPluginAsync(conn, plugin, cmd, args);
 +            // poll every 1 seconds
 +            waitForTask(conn, task, 1000, timeout);
 +            checkForSuccess(conn, task);
 +            final String result = task.getResult(conn);
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin Result: " + result);
 +            }
 +            return result.replace("<value>", "").replace("</value>", "").replace("\n", "");
 +        } catch (final Types.HandleInvalid e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to HandleInvalid clazz:" + e.clazz + ", handle:" + e.handle);
 +        } catch (final XenAPIException e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.toString(), e);
 +        } catch (final Exception e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.getMessage(), e);
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e1) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e1.toString());
 +                }
 +            }
 +        }
 +        return null;
 +    }
 +
 +    public String callHostPluginPremium(final Connection conn, final String cmd, final String... params) {
 +        return callHostPlugin(conn, "vmopspremium", cmd, params);
 +    }
 +
 +    protected String callHostPluginThroughMaster(final Connection conn, final String plugin, final String cmd, final String... params) {
 +        final Map<String, String> args = new HashMap<String, String>();
 +
 +        try {
 +            final Map<Pool, Pool.Record> poolRecs = Pool.getAllRecords(conn);
 +            if (poolRecs.size() != 1) {
 +                throw new CloudRuntimeException("There are " + poolRecs.size() + " pool for host :" + _host.getUuid());
 +            }
 +            final Host master = poolRecs.values().iterator().next().master;
 +            for (int i = 0; i < params.length; i += 2) {
 +                args.put(params[i], params[i + 1]);
 +            }
 +
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin executing for command " + cmd + " with " + getArgsString(args));
 +            }
 +            final String result = master.callPlugin(conn, plugin, cmd, args);
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("callHostPlugin Result: " + result);
 +            }
 +            return result.replace("\n", "");
 +        } catch (final Types.HandleInvalid e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to HandleInvalid clazz:" + e.clazz + ", handle:" + e.handle);
 +        } catch (final XenAPIException e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.toString(), e);
 +        } catch (final XmlRpcException e) {
 +            s_logger.warn("callHostPlugin failed for cmd: " + cmd + " with args " + getArgsString(args) + " due to " + e.getMessage(), e);
 +        }
 +        return null;
 +    }
 +
 +    public boolean canBridgeFirewall() {
 +        return _canBridgeFirewall;
 +    }
 +
 +    public boolean canBridgeFirewall(final Connection conn) {
 +        return Boolean.valueOf(callHostPlugin(conn, "vmops", "can_bridge_firewall", "host_uuid", _host.getUuid(), "instance", _instance));
 +    }
 +
 +    public void checkForSuccess(final Connection c, final Task task) throws XenAPIException, XmlRpcException {
 +        if (task.getStatus(c) == Types.TaskStatusType.SUCCESS) {
 +            if (s_logger.isTraceEnabled()) {
 +                s_logger.trace("Task " + task.getNameLabel(c) + " (" + task.getUuid(c) + ") completed");
 +            }
 +            return;
 +        } else {
 +            final String msg = "Task failed! Task record: " + task.getRecord(c);
 +            s_logger.warn(msg);
 +            task.cancel(c);
 +            task.destroy(c);
 +            throw new Types.BadAsyncResult(msg);
 +        }
 +    }
 +
 +    protected boolean checkSR(final Connection conn, final SR sr) {
 +        try {
 +            final SR.Record srr = sr.getRecord(conn);
 +            final Set<PBD> pbds = sr.getPBDs(conn);
 +            if (pbds.size() == 0) {
 +                final String msg = "There is no PBDs for this SR: " + srr.nameLabel + " on host:" + _host.getUuid();
 +                s_logger.warn(msg);
 +                return false;
 +            }
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Checking " + srr.nameLabel + " or SR " + srr.uuid + " on " + _host);
 +            }
 +            if (srr.shared) {
 +                if (SRType.NFS.equals(srr.type)) {
 +                    final Map<String, String> smConfig = srr.smConfig;
 +                    if (!smConfig.containsKey("nosubdir")) {
 +                        smConfig.put("nosubdir", "true");
 +                        sr.setSmConfig(conn, smConfig);
 +                    }
 +                }
 +
 +                final Host host = Host.getByUuid(conn, _host.getUuid());
 +                boolean found = false;
 +                for (final PBD pbd : pbds) {
 +                    final PBD.Record pbdr = pbd.getRecord(conn);
 +                    if (host.equals(pbdr.host)) {
 +                        if (!pbdr.currentlyAttached) {
 +                            pbdPlug(conn, pbd, pbdr.uuid);
 +                        }
 +                        found = true;
 +                        break;
 +                    }
 +                }
 +                if (!found) {
 +                    final PBD.Record pbdr = srr.PBDs.iterator().next().getRecord(conn);
 +                    pbdr.host = host;
 +                    pbdr.uuid = "";
 +                    final PBD pbd = PBD.create(conn, pbdr);
 +                    pbdPlug(conn, pbd, pbd.getUuid(conn));
 +                }
 +            } else {
 +                for (final PBD pbd : pbds) {
 +                    final PBD.Record pbdr = pbd.getRecord(conn);
 +                    if (!pbdr.currentlyAttached) {
 +                        pbdPlug(conn, pbd, pbdr.uuid);
 +                    }
 +                }
 +            }
 +
 +        } catch (final Exception e) {
 +            final String msg = "checkSR failed host:" + _host + " due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    private void CheckXenHostInfo() throws ConfigurationException {
 +        final Connection conn = ConnPool.getConnect(_host.getIp(), _username, _password);
 +        if (conn == null) {
 +            throw new ConfigurationException("Can not create connection to " + _host.getIp());
 +        }
 +        try {
 +            Host.Record hostRec = null;
 +            try {
 +                final Host host = Host.getByUuid(conn, _host.getUuid());
 +                hostRec = host.getRecord(conn);
 +                final Pool.Record poolRec = Pool.getAllRecords(conn).values().iterator().next();
 +                _host.setPool(poolRec.uuid);
 +
 +            } catch (final Exception e) {
 +                throw new ConfigurationException("Can not get host information from " + _host.getIp());
 +            }
 +            if (!hostRec.address.equals(_host.getIp())) {
 +                final String msg = "Host " + _host.getIp() + " seems be reinstalled, please remove this host and readd";
 +                s_logger.error(msg);
 +                throw new ConfigurationException(msg);
 +            }
 +        } finally {
 +            try {
 +                Session.logout(conn);
 +            } catch (final Exception e) {
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public ExecutionResult cleanupCommand(final NetworkElementCommand cmd) {
 +        if (cmd instanceof IpAssocCommand && !(cmd instanceof IpAssocVpcCommand)) {
 +            return cleanupNetworkElementCommand((IpAssocCommand)cmd);
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    public boolean cleanupHaltedVms(final Connection conn) throws XenAPIException, XmlRpcException {
 +        final Host host = Host.getByUuid(conn, _host.getUuid());
 +        final Map<VM, VM.Record> vms = VM.getAllRecords(conn);
 +        boolean success = true;
 +        if (vms != null && !vms.isEmpty()) {
 +            for (final Map.Entry<VM, VM.Record> entry : vms.entrySet()) {
 +                final VM vm = entry.getKey();
 +                final VM.Record vmRec = entry.getValue();
 +                if (vmRec.isATemplate || vmRec.isControlDomain) {
 +                    continue;
 +                }
 +
 +                if (VmPowerState.HALTED.equals(vmRec.powerState) && vmRec.affinity.equals(host) && !isAlienVm(vm, conn)) {
 +                    try {
 +                        vm.destroy(conn);
 +                    } catch (final Exception e) {
 +                        s_logger.warn("Catch Exception " + e.getClass().getName() + ": unable to destroy VM " + vmRec.nameLabel + " due to ", e);
 +                        success = false;
 +                    }
 +                }
 +            }
 +        }
 +        return success;
 +    }
 +
 +    protected ExecutionResult cleanupNetworkElementCommand(final IpAssocCommand cmd) {
 +        final Connection conn = getConnection();
 +        final String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
 +        final String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
 +        final String lastIp = cmd.getAccessDetail(NetworkElementCommand.NETWORK_PUB_LAST_IP);
 +
 +        try {
 +            final IpAddressTO[] ips = cmd.getIpAddresses();
 +            for (final IpAddressTO ip : ips) {
 +
 +                final VM router = getVM(conn, routerName);
 +
 +                final NicTO nic = new NicTO();
 +                nic.setMac(ip.getVifMacAddress());
 +                nic.setType(ip.getTrafficType());
 +                if (ip.getBroadcastUri() == null) {
 +                    nic.setBroadcastType(BroadcastDomainType.Native);
 +                } else {
 +                    final URI uri = BroadcastDomainType.fromString(ip.getBroadcastUri());
 +                    nic.setBroadcastType(BroadcastDomainType.getSchemeValue(uri));
 +                    nic.setBroadcastUri(uri);
 +                }
 +                nic.setDeviceId(0);
 +                nic.setNetworkRateMbps(ip.getNetworkRate());
 +                nic.setName(ip.getNetworkName());
 +
 +                Network network = getNetwork(conn, nic);
 +
 +                // If we are disassociating the last IP address in the VLAN, we
 +                // need
 +                // to remove a VIF
 +                boolean removeVif = false;
 +
 +                // there is only one ip in this public vlan and removing it, so
 +                // remove the nic
 +                if (org.apache.commons.lang.StringUtils.equalsIgnoreCase(lastIp, "true") && !ip.isAdd()) {
 +                    final VIF correctVif = getCorrectVif(conn, router, network);
 +                    // in isolated network eth2 is the default public interface. We don't want to delete it.
 +                    if (correctVif != null && !correctVif.getDevice(conn).equals("2")) {
 +                        removeVif = true;
 +                    }
 +                }
 +
 +                if (removeVif) {
 +
 +                    // Determine the correct VIF on DomR to
 +                    // associate/disassociate the
 +                    // IP address with
 +
 +                    final VIF correctVif = getCorrectVif(conn, router, network);
 +                    if (correctVif != null) {
 +                        network = correctVif.getNetwork(conn);
 +
 +                        // Mark this vif to be removed from network usage
 +                        networkUsage(conn, routerIp, "deleteVif", "eth" + correctVif.getDevice(conn));
 +
 +                        // Remove the VIF from DomR
 +                        correctVif.unplug(conn);
 +                        correctVif.destroy(conn);
 +
 +                        // Disable the VLAN network if necessary
 +                        disableVlanNetwork(conn, network);
 +                    }
 +                }
 +            }
 +        } catch (final Exception e) {
 +            s_logger.debug("Ip Assoc failure on applying one ip due to exception:  ", e);
 +            return new ExecutionResult(false, e.getMessage());
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    public void cleanupTemplateSR(final Connection conn) {
 +        Set<PBD> pbds = null;
 +        try {
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            pbds = host.getPBDs(conn);
 +        } catch (final XenAPIException e) {
 +            s_logger.warn("Unable to get the SRs " + e.toString(), e);
 +            throw new CloudRuntimeException("Unable to get SRs " + e.toString(), e);
 +        } catch (final Exception e) {
 +            throw new CloudRuntimeException("Unable to get SRs " + e.getMessage(), e);
 +        }
 +        for (final PBD pbd : pbds) {
 +            SR sr = null;
 +            SR.Record srRec = null;
 +            try {
 +                sr = pbd.getSR(conn);
 +                srRec = sr.getRecord(conn);
 +            } catch (final Exception e) {
 +                s_logger.warn("pbd.getSR get Exception due to ", e);
 +                continue;
 +            }
 +            final String type = srRec.type;
 +            if (srRec.shared) {
 +                continue;
 +            }
 +            if (SRType.NFS.equals(type) || SRType.ISO.equals(type) && srRec.nameDescription.contains("template")) {
 +                try {
 +                    pbd.unplug(conn);
 +                    pbd.destroy(conn);
 +                    sr.forget(conn);
 +                } catch (final Exception e) {
 +                    s_logger.warn("forget SR catch Exception due to ", e);
 +                }
 +            }
 +        }
 +    }
 +
 +    public void cleanUpTmpDomVif(final Connection conn, final Network nw) throws XenAPIException, XmlRpcException {
 +
 +        final Pair<VM, VM.Record> vm = getControlDomain(conn);
 +        final VM dom0 = vm.first();
 +        final Set<VIF> dom0Vifs = dom0.getVIFs(conn);
 +        for (final VIF v : dom0Vifs) {
 +            String vifName = "unknown";
 +            try {
 +                final VIF.Record vifr = v.getRecord(conn);
 +                if (v.getNetwork(conn).getUuid(conn).equals(nw.getUuid(conn))) {
 +                    if (vifr != null) {
 +                        final Map<String, String> config = vifr.otherConfig;
 +                        vifName = config.get("nameLabel");
 +                    }
 +                    s_logger.debug("A VIF in dom0 for the network is found - so destroy the vif");
 +                    v.destroy(conn);
 +                    s_logger.debug("Destroy temp dom0 vif" + vifName + " success");
 +                }
 +            } catch (final Exception e) {
 +                s_logger.warn("Destroy temp dom0 vif " + vifName + "failed", e);
 +            }
 +        }
 +    }
 +
 +    protected VDI cloudVDIcopy(final Connection conn, final VDI vdi, final SR sr, int wait) throws Exception {
 +        Task task = null;
 +        if (wait == 0) {
 +            wait = 2 * 60 * 60;
 +        }
 +        try {
 +            task = vdi.copyAsync(conn, sr);
 +            // poll every 1 seconds , timeout after 2 hours
 +            waitForTask(conn, task, 1000, (long)wait * 1000);
 +            checkForSuccess(conn, task);
 +            final VDI dvdi = Types.toVDI(task, conn);
 +            return dvdi;
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e.toString());
 +                }
 +            }
 +        }
 +    }
 +
 +    public HashMap<String, String> clusterVMMetaDataSync(final Connection conn) {
 +        final HashMap<String, String> vmMetaDatum = new HashMap<String, String>();
 +        try {
 +            final Map<VM, VM.Record> vm_map = VM.getAllRecords(conn); // USE
 +            // THIS TO
 +            // GET ALL
 +            // VMS
 +            // FROM A
 +            // CLUSTER
 +            if (vm_map != null) {
 +                for (final VM.Record record : vm_map.values()) {
 +                    if (record.isControlDomain || record.isASnapshot || record.isATemplate) {
 +                        continue; // Skip DOM0
 +                    }
 +                    final String platform = StringUtils.mapToString(record.platform);
 +                    if (platform.isEmpty()) {
 +                        continue; //Skip if platform is null
 +                    }
 +                    vmMetaDatum.put(record.nameLabel, StringUtils.mapToString(record.platform));
 +                }
 +            }
 +        } catch (final Throwable e) {
 +            final String msg = "Unable to get vms through host " + _host.getUuid() + " due to to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg);
 +        }
 +        return vmMetaDatum;
 +    }
 +
 +    @Override
 +    public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
 +        _name = name;
 +
 +        try {
 +            _dcId = Long.parseLong((String)params.get("zone"));
 +        } catch (final NumberFormatException e) {
 +            throw new ConfigurationException("Unable to get the zone " + params.get("zone"));
 +        }
 +
 +        _host.setUuid((String)params.get("guid"));
 +
 +        _name = _host.getUuid();
 +        _host.setIp((String)params.get("ipaddress"));
 +
 +        _username = (String)params.get("username");
 +        _password.add((String)params.get("password"));
 +        _pod = (String)params.get("pod");
 +        _cluster = (String)params.get("cluster");
 +        _privateNetworkName = (String)params.get("private.network.device");
 +        _publicNetworkName = (String)params.get("public.network.device");
 +        _guestNetworkName = (String)params.get("guest.network.device");
 +        _instance = (String)params.get("instance.name");
 +        _securityGroupEnabled = Boolean.parseBoolean((String)params.get("securitygroupenabled"));
 +
 +        _linkLocalPrivateNetworkName = (String)params.get("private.linkLocal.device");
 +        if (_linkLocalPrivateNetworkName == null) {
 +            _linkLocalPrivateNetworkName = "cloud_link_local_network";
 +        }
 +
 +        _storageNetworkName1 = (String)params.get("storage.network.device1");
 +        _storageNetworkName2 = (String)params.get("storage.network.device2");
 +
 +        _heartbeatTimeout = NumbersUtil.parseInt((String)params.get("xenserver.heartbeat.timeout"), 120);
 +        _heartbeatInterval = NumbersUtil.parseInt((String)params.get("xenserver.heartbeat.interval"), 60);
 +
 +        String value = (String)params.get("wait");
 +        _wait = NumbersUtil.parseInt(value, 600);
 +
 +        value = (String)params.get("migratewait");
 +        _migratewait = NumbersUtil.parseInt(value, 3600);
 +
 +        _maxNics = NumbersUtil.parseInt((String)params.get("xenserver.nics.max"), 7);
 +
 +        if (_pod == null) {
 +            throw new ConfigurationException("Unable to get the pod");
 +        }
 +
 +        if (_host.getIp() == null) {
 +            throw new ConfigurationException("Unable to get the host address");
 +        }
 +
 +        if (_username == null) {
 +            throw new ConfigurationException("Unable to get the username");
 +        }
 +
 +        if (_password.peek() == null) {
 +            throw new ConfigurationException("Unable to get the password");
 +        }
 +
 +        if (_host.getUuid() == null) {
 +            throw new ConfigurationException("Unable to get the uuid");
 +        }
 +
 +        CheckXenHostInfo();
 +
 +        storageHandler = buildStorageHandler();
 +
 +        _vrResource = new VirtualRoutingResource(this);
 +        if (!_vrResource.configure(name, params)) {
 +            throw new ConfigurationException("Unable to configure VirtualRoutingResource");
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * This method creates a XenServer network and configures it for being used
 +     * as a L2-in-L3 tunneled network
 +     */
 +    public synchronized Network configureTunnelNetwork(final Connection conn, final Long networkId, final long hostId, final String bridgeName) {
 +        try {
 +            final Network nw = findOrCreateTunnelNetwork(conn, bridgeName);
 +            // Invoke plugin to setup the bridge which will be used by this
 +            // network
 +            final String bridge = nw.getBridge(conn);
 +            final Map<String, String> nwOtherConfig = nw.getOtherConfig(conn);
 +            final String configuredHosts = nwOtherConfig.get("ovs-host-setup");
 +            boolean configured = false;
 +            if (configuredHosts != null) {
 +                final String hostIdsStr[] = configuredHosts.split(",");
 +                for (final String hostIdStr : hostIdsStr) {
 +                    if (hostIdStr.equals(((Long)hostId).toString())) {
 +                        configured = true;
 +                        break;
 +                    }
 +                }
 +            }
 +
 +            if (!configured) {
 +                String result;
 +                if (bridgeName.startsWith("OVS-DR-VPC-Bridge")) {
 +                    result = callHostPlugin(conn, "ovstunnel", "setup_ovs_bridge_for_distributed_routing", "bridge", bridge, "key", bridgeName, "xs_nw_uuid", nw.getUuid(conn), "cs_host_id",
 +                            ((Long)hostId).toString());
 +                } else {
 +                    result = callHostPlugin(conn, "ovstunnel", "setup_ovs_bridge", "bridge", bridge, "key", bridgeName, "xs_nw_uuid", nw.getUuid(conn), "cs_host_id", ((Long)hostId).toString());
 +                }
 +
 +                // Note down the fact that the ovs bridge has been setup
 +                final String[] res = result.split(":");
 +                if (res.length != 2 || !res[0].equalsIgnoreCase("SUCCESS")) {
 +                    throw new CloudRuntimeException("Unable to pre-configure OVS bridge " + bridge);
 +                }
 +            }
 +            return nw;
 +        } catch (final Exception e) {
 +            s_logger.warn("createandConfigureTunnelNetwork failed", e);
 +            return null;
 +        }
 +    }
 +
 +    public String connect(final Connection conn, final String vmname, final String ipAddress) {
 +        return connect(conn, vmname, ipAddress, 3922);
 +    }
 +
 +    public String connect(final Connection conn, final String vmName, final String ipAddress, final int port) {
 +        for (int i = 0; i <= _retry; i++) {
 +            try {
 +                final Set<VM> vms = VM.getByNameLabel(conn, vmName);
 +                if (vms.size() < 1) {
 +                    final String msg = "VM " + vmName + " is not running";
 +                    s_logger.warn(msg);
 +                    return msg;
 +                }
 +            } catch (final Exception e) {
 +                final String msg = "VM.getByNameLabel " + vmName + " failed due to " + e.toString();
 +                s_logger.warn(msg, e);
 +                return msg;
 +            }
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Trying to connect to " + ipAddress + " attempt " + i + " of " + _retry);
 +            }
 +            if (pingdomr(conn, ipAddress, Integer.toString(port))) {
 +                return null;
 +            }
 +            try {
 +                Thread.sleep(_sleep);
 +            } catch (final InterruptedException e) {
 +            }
 +        }
 +        final String msg = "Timeout, Unable to logon to " + ipAddress;
 +        s_logger.debug(msg);
 +
 +        return msg;
 +    }
 +
 +    public String copyVhdFromSecondaryStorage(final Connection conn, final String mountpoint, final String sruuid, final int wait) {
 +        final String nameLabel = "cloud-" + UUID.randomUUID().toString();
 +        final String results = callHostPluginAsync(conn, "vmopspremium", "copy_vhd_from_secondarystorage", wait, "mountpoint", mountpoint, "sruuid", sruuid, "namelabel", nameLabel);
 +        String errMsg = null;
 +        if (results == null || results.isEmpty()) {
 +            errMsg = "copy_vhd_from_secondarystorage return null";
 +        } else {
 +            final String[] tmp = results.split("#");
 +            final String status = tmp[0];
 +            if (status.equals("0")) {
 +                return tmp[1];
 +            } else {
 +                errMsg = tmp[1];
 +            }
 +        }
 +        final String source = mountpoint.substring(mountpoint.lastIndexOf('/') + 1);
 +        if (killCopyProcess(conn, source)) {
 +            destroyVDIbyNameLabel(conn, nameLabel);
 +        }
 +        s_logger.warn(errMsg);
 +        throw new CloudRuntimeException(errMsg);
 +    }
 +
 +    @Override
 +    public ExecutionResult createFileInVR(final String routerIp, final String path, final String filename, final String content) {
 +        final Connection conn = getConnection();
 +        final String hostPath = "/tmp/";
 +
 +        s_logger.debug("Copying VR with ip " + routerIp + " config file into host " + _host.getIp());
 +        try {
 +            SshHelper.scpTo(_host.getIp(), 22, _username, null, _password.peek(), hostPath, content.getBytes(Charset.defaultCharset()), filename, null);
 +        } catch (final Exception e) {
 +            s_logger.warn("scp VR config file into host " + _host.getIp() + " failed with exception " + e.getMessage().toString());
 +        }
 +
 +        final String rc = callHostPlugin(conn, "vmops", "createFileInDomr", "domrip", routerIp, "srcfilepath", hostPath + filename, "dstfilepath", path);
 +        s_logger.debug("VR Config file " + filename + " got created in VR, ip " + routerIp + " with content \n" + content);
 +
 +        return new ExecutionResult(rc.startsWith("succ#"), rc.substring(5));
 +    }
 +
 +    protected SR createIsoSRbyURI(final Connection conn, final URI uri, final String vmName, final boolean shared) {
 +        try {
 +            final Map<String, String> deviceConfig = new HashMap<String, String>();
 +            String path = uri.getPath();
 +            path = path.replace("//", "/");
 +            deviceConfig.put("location", uri.getHost() + ":" + path);
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            final SR sr = SR.create(conn, host, deviceConfig, new Long(0), uri.getHost() + path, "iso", "iso", "iso", shared, new HashMap<String, String>());
 +            sr.setNameLabel(conn, vmName + "-ISO");
 +            sr.setNameDescription(conn, deviceConfig.get("location"));
 +
 +            sr.scan(conn);
 +            return sr;
 +        } catch (final XenAPIException e) {
 +            final String msg = "createIsoSRbyURI failed! mountpoint: " + uri.getHost() + uri.getPath() + " due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        } catch (final Exception e) {
 +            final String msg = "createIsoSRbyURI failed! mountpoint: " + uri.getHost() + uri.getPath() + " due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        }
 +    }
 +
 +    protected SR createNfsSRbyURI(final Connection conn, final URI uri, final boolean shared) {
 +        try {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Creating a " + (shared ? "shared SR for " : "not shared SR for ") + uri);
 +            }
 +
 +            final Map<String, String> deviceConfig = new HashMap<String, String>();
 +            String path = uri.getPath();
 +            path = path.replace("//", "/");
 +            deviceConfig.put("server", uri.getHost());
 +            deviceConfig.put("serverpath", path);
 +            final String name = UUID.nameUUIDFromBytes(new String(uri.getHost() + path).getBytes()).toString();
 +            if (!shared) {
 +                final Set<SR> srs = SR.getByNameLabel(conn, name);
 +                for (final SR sr : srs) {
 +                    final SR.Record record = sr.getRecord(conn);
 +                    if (SRType.NFS.equals(record.type) && record.contentType.equals("user") && !record.shared) {
 +                        removeSRSync(conn, sr);
 +                    }
 +                }
 +            }
 +
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            final Map<String, String> smConfig = new HashMap<String, String>();
 +            smConfig.put("nosubdir", "true");
 +            final SR sr = SR.create(conn, host, deviceConfig, new Long(0), name, uri.getHost() + uri.getPath(), SRType.NFS.toString(), "user", shared, smConfig);
 +
 +            if (!checkSR(conn, sr)) {
 +                throw new Exception("no attached PBD");
 +            }
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug(logX(sr, "Created a SR; UUID is " + sr.getUuid(conn) + " device config is " + deviceConfig));
 +            }
 +            sr.scan(conn);
 +            return sr;
 +        } catch (final XenAPIException e) {
 +            final String msg = "Can not create second storage SR mountpoint: " + uri.getHost() + uri.getPath() + " due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        } catch (final Exception e) {
 +            final String msg = "Can not create second storage SR mountpoint: " + uri.getHost() + uri.getPath() + " due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        }
 +    }
 +
 +    public VBD createPatchVbd(final Connection conn, final String vmName, final VM vm) throws XmlRpcException, XenAPIException {
 +
 +        if (_host.getSystemvmisouuid() == null) {
 +            final Set<SR> srs = SR.getByNameLabel(conn, "XenServer Tools");
 +            if (srs.size() != 1) {
 +                throw new CloudRuntimeException("There are " + srs.size() + " SRs with name XenServer Tools");
 +            }
 +            final SR sr = srs.iterator().next();
 +            sr.scan(conn);
 +
 +            final SR.Record srr = sr.getRecord(conn);
 +
 +            if (_host.getSystemvmisouuid() == null) {
 +                for (final VDI vdi : srr.VDIs) {
 +                    final VDI.Record vdir = vdi.getRecord(conn);
 +                    if (vdir.nameLabel.contains("systemvm.iso")) {
 +                        _host.setSystemvmisouuid(vdir.uuid);
 +                        break;
 +                    }
 +                }
 +            }
 +            if (_host.getSystemvmisouuid() == null) {
 +                throw new CloudRuntimeException("can not find systemvmiso");
 +            }
 +        }
 +
 +        final VBD.Record cdromVBDR = new VBD.Record();
 +        cdromVBDR.VM = vm;
 +        cdromVBDR.empty = true;
 +        cdromVBDR.bootable = false;
 +        cdromVBDR.userdevice = "3";
 +        cdromVBDR.mode = Types.VbdMode.RO;
 +        cdromVBDR.type = Types.VbdType.CD;
 +        final VBD cdromVBD = VBD.create(conn, cdromVBDR);
 +        cdromVBD.insert(conn, VDI.getByUuid(conn, _host.getSystemvmisouuid()));
 +
 +        return cdromVBD;
 +    }
 +
 +    protected boolean createSecondaryStorageFolder(final Connection conn, final String remoteMountPath, final String newFolder) {
 +        final String result = callHostPlugin(conn, "vmopsSnapshot", "create_secondary_storage_folder", "remoteMountPath", remoteMountPath, "newFolder", newFolder);
 +        return result != null;
 +    }
 +
 +    String createTemplateFromSnapshot(final Connection conn, final String templatePath, final String snapshotPath, final int wait) {
 +        final String tmpltLocalDir = UUID.randomUUID().toString();
 +        final String results = callHostPluginAsync(conn, "vmopspremium", "create_privatetemplate_from_snapshot", wait, "templatePath", templatePath, "snapshotPath", snapshotPath, "tmpltLocalDir",
 +                tmpltLocalDir);
 +        String errMsg = null;
 +        if (results == null || results.isEmpty()) {
 +            errMsg = "create_privatetemplate_from_snapshot return null";
 +        } else {
 +            final String[] tmp = results.split("#");
 +            final String status = tmp[0];
 +            if (status.equals("0")) {
 +                return results;
 +            } else {
 +                errMsg = "create_privatetemplate_from_snapshot failed due to " + tmp[1];
 +            }
 +        }
 +        final String source = "cloud_mount/" + tmpltLocalDir;
 +        killCopyProcess(conn, source);
 +        s_logger.warn(errMsg);
 +        throw new CloudRuntimeException(errMsg);
 +    }
 +
 +    public VBD createVbd(final Connection conn, final DiskTO volume, final String vmName, final VM vm, final BootloaderType bootLoaderType, VDI vdi) throws XmlRpcException, XenAPIException {
 +        final Volume.Type type = volume.getType();
 +
 +        if (vdi == null) {
 +            vdi = mount(conn, vmName, volume);
 +        }
 +
 +        if (vdi != null) {
 +            if ("detached".equals(vdi.getNameLabel(conn))) {
 +                vdi.setNameLabel(conn, vmName + "-DATA");
 +            }
 +
 +            final Map<String, String> smConfig = vdi.getSmConfig(conn);
 +            for (final String key : smConfig.keySet()) {
 +                if (key.startsWith("host_")) {
 +                    vdi.removeFromSmConfig(conn, key);
 +                    break;
 +                }
 +            }
 +        }
 +        final VBD.Record vbdr = new VBD.Record();
 +        vbdr.VM = vm;
 +        if (vdi != null) {
 +            vbdr.VDI = vdi;
 +        } else {
 +            vbdr.empty = true;
 +        }
 +        if (type == Volume.Type.ROOT && bootLoaderType == BootloaderType.PyGrub) {
 +            vbdr.bootable = true;
 +        } else if (type == Volume.Type.ISO && bootLoaderType == BootloaderType.CD) {
 +            vbdr.bootable = true;
 +        }
 +
 +        if (volume.getType() == Volume.Type.ISO) {
 +            vbdr.mode = Types.VbdMode.RO;
 +            vbdr.type = Types.VbdType.CD;
 +            vbdr.userdevice = "3";
 +        } else {
 +            vbdr.mode = Types.VbdMode.RW;
 +            vbdr.type = Types.VbdType.DISK;
 +            vbdr.unpluggable = (volume.getType() == Volume.Type.ROOT) ? false : true;
 +            vbdr.userdevice = "autodetect";
 +            final Long deviceId = volume.getDiskSeq();
 +            if (deviceId != null && (!isDeviceUsed(conn, vm, deviceId) || deviceId > 3)) {
 +                vbdr.userdevice = deviceId.toString();
 +            }
 +        }
 +        final VBD vbd = VBD.create(conn, vbdr);
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("VBD " + vbd.getUuid(conn) + " created for " + volume);
 +        }
 +
 +        return vbd;
 +    }
 +
 +    public VDI createVdi(final SR sr, final String vdiNameLabel, final Long volumeSize) throws Types.XenAPIException, XmlRpcException {
 +        final Connection conn = getConnection();
 +
 +        final VDI.Record vdir = new VDI.Record();
 +
 +        vdir.nameLabel = vdiNameLabel;
 +        vdir.SR = sr;
 +        vdir.type = Types.VdiType.USER;
 +
 +        final long totalSrSpace = sr.getPhysicalSize(conn);
 +        final long unavailableSrSpace = sr.getPhysicalUtilisation(conn);
 +        final long availableSrSpace = totalSrSpace - unavailableSrSpace;
 +
 +        if (availableSrSpace < volumeSize) {
 +            throw new CloudRuntimeException("Available space for SR cannot be less than " + volumeSize + ".");
 +        }
 +
 +        vdir.virtualSize = volumeSize;
 +
 +        return VDI.create(conn, vdir);
 +    }
 +
 +    public void createVGPU(final Connection conn, final StartCommand cmd, final VM vm, final GPUDeviceTO gpuDevice) throws XenAPIException, XmlRpcException {
 +    }
 +
 +    public VIF createVif(final Connection conn, final String vmName, final VM vm, final VirtualMachineTO vmSpec, final NicTO nic) throws XmlRpcException, XenAPIException {
 +        assert nic.getUuid() != null : "Nic should have a uuid value";
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Creating VIF for " + vmName + " on nic " + nic);
 +        }
 +        VIF.Record vifr = new VIF.Record();
 +        vifr.VM = vm;
 +        vifr.device = Integer.toString(nic.getDeviceId());
 +        vifr.MAC = nic.getMac();
 +
 +        // Nicira needs these IDs to find the NIC
 +        vifr.otherConfig = new HashMap<String, String>();
 +        vifr.otherConfig.put("nicira-iface-id", nic.getUuid());
 +        vifr.otherConfig.put("nicira-vm-id", vm.getUuid(conn));
 +        // Provide XAPI with the cloudstack vm and nic uids.
 +        vifr.otherConfig.put("cloudstack-nic-id", nic.getUuid());
 +        if (vmSpec != null) {
 +            vifr.otherConfig.put("cloudstack-vm-id", vmSpec.getUuid());
 +        }
 +
 +        // OVS plugin looks at network UUID in the vif 'otherconfig' details to
 +        // group VIF's & tunnel ports as part of tier
 +        // when bridge is setup for distributed routing
 +        vifr.otherConfig.put("cloudstack-network-id", nic.getNetworkUuid());
 +
 +        // Nuage Vsp needs Virtual Router IP to be passed in the otherconfig
 +        // get the virtual router IP information from broadcast uri
 +        final URI broadcastUri = nic.getBroadcastUri();
 +        if (broadcastUri != null && broadcastUri.getScheme().equalsIgnoreCase(Networks.BroadcastDomainType.Vsp.scheme())) {
 +            final String path = broadcastUri.getPath();
 +            vifr.otherConfig.put("vsp-vr-ip", path.substring(1));
 +        }
 +        vifr.network = getNetwork(conn, nic);
 +
 +        if (nic.getNetworkRateMbps() != null && nic.getNetworkRateMbps().intValue() != -1) {
 +            vifr.qosAlgorithmType = "ratelimit";
 +            vifr.qosAlgorithmParams = new HashMap<String, String>();
 +            // convert mbs to kilobyte per second
 +            vifr.qosAlgorithmParams.put("kbps", Integer.toString(nic.getNetworkRateMbps() * 128));
 +        }
 +
 +        vifr.lockingMode = Types.VifLockingMode.NETWORK_DEFAULT;
 +        final VIF vif = VIF.create(conn, vifr);
 +        if (s_logger.isDebugEnabled()) {
 +            vifr = vif.getRecord(conn);
 +            if (vifr != null) {
 +                s_logger.debug("Created a vif " + vifr.uuid + " on " + nic.getDeviceId());
 +            }
 +        }
 +
 +        return vif;
 +    }
 +
 +    public VM createVmFromTemplate(final Connection conn, final VirtualMachineTO vmSpec, final Host host) throws XenAPIException, XmlRpcException {
 +        final String guestOsTypeName = getGuestOsType(vmSpec.getPlatformEmulator());
 +        final Set<VM> templates = VM.getByNameLabel(conn, guestOsTypeName);
 +        if (templates == null || templates.isEmpty()) {
 +            throw new CloudRuntimeException("Cannot find template " + guestOsTypeName + " on XenServer host");
 +        }
 +        assert templates.size() == 1 : "Should only have 1 template but found " + templates.size();
 +        final VM template = templates.iterator().next();
 +
 +        final VM.Record vmr = template.getRecord(conn);
 +        vmr.affinity = host;
 +        vmr.otherConfig.remove("disks");
 +        vmr.otherConfig.remove("default_template");
 +        vmr.otherConfig.remove("mac_seed");
 +        vmr.isATemplate = false;
 +        vmr.nameLabel = vmSpec.getName();
 +        vmr.actionsAfterCrash = Types.OnCrashBehaviour.DESTROY;
 +        vmr.actionsAfterShutdown = Types.OnNormalExit.DESTROY;
 +        vmr.otherConfig.put("vm_uuid", vmSpec.getUuid());
 +        vmr.VCPUsMax = (long)vmSpec.getCpus(); // FIX ME: In case of dynamic
 +        // scaling this VCPU max should
 +        // be the minumum of
 +        // recommended value for that template and capacity remaining on host
 +
 +        long recommendedMemoryMin = 0l;
 +        long recommendedMemoryMax = 0l;
 +
 +        Map<String, String> guestOsDetails = vmSpec.getGuestOsDetails();
 +
 +        if (guestOsDetails != null) {
 +            if (guestOsDetails.containsKey("xenserver.dynamicMin")) {
 +                recommendedMemoryMin = Long.valueOf(guestOsDetails.get("xenserver.dynamicMin")).longValue();
 +            }
 +
 +            if (guestOsDetails.containsKey("xenserver.dynamicMax")) {
 +                recommendedMemoryMax = Long.valueOf(guestOsDetails.get("xenserver.dynamicMax")).longValue();
 +            }
- 
 +        }
 +
 +        if (isDmcEnabled(conn, host) && vmSpec.isEnableDynamicallyScaleVm()) {
 +            // scaling is allowed
 +            vmr.memoryStaticMin = getStaticMin(vmSpec.getOs(), vmSpec.getBootloader() == BootloaderType.CD, vmSpec.getMinRam(), vmSpec.getMaxRam(), recommendedMemoryMin);
 +            vmr.memoryStaticMax = getStaticMax(vmSpec.getOs(), vmSpec.getBootloader() == BootloaderType.CD, vmSpec.getMinRam(), vmSpec.getMaxRam(), recommendedMemoryMax);
 +            vmr.memoryDynamicMin = vmSpec.getMinRam();
 +            vmr.memoryDynamicMax = vmSpec.getMaxRam();
 +            if (guestOsTypeName.toLowerCase().contains("windows")) {
 +                vmr.VCPUsMax = (long)vmSpec.getCpus();
 +            } else {
 +                if (vmSpec.getVcpuMaxLimit() != null) {
 +                    vmr.VCPUsMax = (long)vmSpec.getVcpuMaxLimit();
 +                }
 +            }
 +        } else {
 +            // scaling disallowed, set static memory target
 +            if (vmSpec.isEnableDynamicallyScaleVm() && !isDmcEnabled(conn, host)) {
 +                s_logger.warn("Host " + host.getHostname(conn) + " does not support dynamic scaling, so the vm " + vmSpec.getName() + " is not dynamically scalable");
 +            }
 +            vmr.memoryStaticMin = vmSpec.getMinRam();
 +            vmr.memoryStaticMax = vmSpec.getMaxRam();
 +            vmr.memoryDynamicMin = vmSpec.getMinRam();
-             ;
 +            vmr.memoryDynamicMax = vmSpec.getMaxRam();
 +
 +            vmr.VCPUsMax = (long)vmSpec.getCpus();
 +        }
 +
 +        vmr.VCPUsAtStartup = (long)vmSpec.getCpus();
 +        vmr.consoles.clear();
 +        vmr.xenstoreData.clear();
 +        //Add xenstore data for the NetscalerVM
 +        if (vmSpec.getType() == VirtualMachine.Type.NetScalerVm) {
 +            NicTO mgmtNic = vmSpec.getNics()[0];
 +            if (mgmtNic != null) {
 +                Map<String, String> xenstoreData = new HashMap<String, String>(3);
-                 xenstoreData.put("vm-data/ip", mgmtNic.getIp().toString().trim());
-                 xenstoreData.put("vm-data/gateway", mgmtNic.getGateway().toString().trim());
-                 xenstoreData.put("vm-data/netmask", mgmtNic.getNetmask().toString().trim());
++                xenstoreData.put(XENSTORE_DATA_IP, mgmtNic.getIp().toString().trim());
++                xenstoreData.put(XENSTORE_DATA_GATEWAY, mgmtNic.getGateway().toString().trim());
++                xenstoreData.put(XENSTORE_DATA_NETMASK, mgmtNic.getNetmask().toString().trim());
 +                vmr.xenstoreData = xenstoreData;
 +            }
 +        }
 +
 +        final VM vm = VM.create(conn, vmr);
-         if (s_logger.isDebugEnabled()) {
-             s_logger.debug("Created VM " + vm.getUuid(conn) + " for " + vmSpec.getName());
-         }
++        s_logger.debug("Created VM " + vm.getUuid(conn) + " for " + vmSpec.getName());
 +
 +        final Map<String, String> vcpuParams = new HashMap<String, String>();
 +
 +        final Integer speed = vmSpec.getMinSpeed();
 +        if (speed != null) {
 +            int cpuWeight = _maxWeight; // cpu_weight
 +            int utilization = 0; // max CPU cap, default is unlimited
 +
 +            // weight based allocation, CPU weight is calculated per VCPU
 +            cpuWeight = (int)(speed * 0.99 / _host.getSpeed() * _maxWeight);
 +            if (cpuWeight > _maxWeight) {
 +                cpuWeight = _maxWeight;
 +            }
 +
 +            if (vmSpec.getLimitCpuUse()) {
 +                // CPU cap is per VM, so need to assign cap based on the number
 +                // of vcpus
 +                utilization = (int)(vmSpec.getMaxSpeed() * 0.99 * vmSpec.getCpus() / _host.getSpeed() * 100);
 +            }
 +
 +            vcpuParams.put("weight", Integer.toString(cpuWeight));
 +            vcpuParams.put("cap", Integer.toString(utilization));
 +
 +        }
 +
 +        if (vcpuParams.size() > 0) {
 +            vm.setVCPUsParams(conn, vcpuParams);
 +        }
 +
 +        final String bootArgs = vmSpec.getBootArgs();
 +        if (bootArgs != null && bootArgs.length() > 0) {
++            // send boot args for PV instances
 +            String pvargs = vm.getPVArgs(conn);
 +            pvargs = pvargs + vmSpec.getBootArgs().replaceAll(" ", "%");
-             if (s_logger.isDebugEnabled()) {
-                 s_logger.debug("PV args are " + pvargs);
-             }
 +            vm.setPVArgs(conn, pvargs);
++            s_logger.debug("PV args are " + pvargs);
++
++            // send boot args into xenstore-data for HVM instances
++            Map<String, String> xenstoreData = new HashMap<>();
++
++            xenstoreData.put(XENSTORE_DATA_CS_INIT, bootArgs);
++            vm.setXenstoreData(conn, xenstoreData);
++            s_logger.debug("HVM args are " + bootArgs);
 +        }
 +
 +        if (!(guestOsTypeName.startsWith("Windows") || guestOsTypeName.startsWith("Citrix") || guestOsTypeName.startsWith("Other"))) {
 +            if (vmSpec.getBootloader() == BootloaderType.CD) {
 +                final DiskTO[] disks = vmSpec.getDisks();
 +                for (final DiskTO disk : disks) {
 +                    if (disk.getType() == Volume.Type.ISO) {
 +                        final TemplateObjectTO iso = (TemplateObjectTO)disk.getData();
 +                        final String osType = iso.getGuestOsType();
 +                        if (osType != null) {
 +                            final String isoGuestOsName = getGuestOsType(vmSpec.getPlatformEmulator());
 +                            if (!isoGuestOsName.equals(guestOsTypeName)) {
 +                                vmSpec.setBootloader(BootloaderType.PyGrub);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            if (vmSpec.getBootloader() == BootloaderType.CD) {
 +                vm.setPVBootloader(conn, "eliloader");
 +                if (!vm.getOtherConfig(conn).containsKey("install-repository")) {
 +                    vm.addToOtherConfig(conn, "install-repository", "cdrom");
 +                }
 +            } else if (vmSpec.getBootloader() == BootloaderType.PyGrub) {
 +                vm.setPVBootloader(conn, "pygrub");
 +                vm.setPVBootloaderArgs(conn, CitrixHelper.getPVbootloaderArgs(guestOsTypeName));
 +            } else {
 +                vm.destroy(conn);
 +                throw new CloudRuntimeException("Unable to handle boot loader type: " + vmSpec.getBootloader());
 +            }
 +        }
 +        try {
 +            finalizeVmMetaData(vm, conn, vmSpec);
 +        } catch (final Exception e) {
 +            throw new CloudRuntimeException("Unable to finalize VM MetaData: " + vmSpec);
 +        }
 +        return vm;
 +    }
 +
 +    public VM createWorkingVM(final Connection conn, final String vmName, final String guestOSType, final String platformEmulator, final List<VolumeObjectTO> listVolumeTo)
 +            throws BadServerResponse, Types.VmBadPowerState, Types.SrFull, Types.OperationNotAllowed, XenAPIException, XmlRpcException {
 +        // below is redundant but keeping for consistency and code readabilty
 +        final String guestOsTypeName = platformEmulator;
 +        if (guestOsTypeName == null) {
 +            final String msg = " Hypervisor " + this.getClass().getName() + " doesn't support guest OS type " + guestOSType + ". you can choose 'Other install media' to run it as HVM";
 +            s_logger.warn(msg);
 +            throw new CloudRuntimeException(msg);
 +        }
 +        final VM template = getVM(conn, guestOsTypeName);
 +        final VM vm = template.createClone(conn, vmName);
 +        vm.setIsATemplate(conn, false);
 +        final Map<VDI, VolumeObjectTO> vdiMap = new HashMap<VDI, VolumeObjectTO>();
 +        for (final VolumeObjectTO volume : listVolumeTo) {
 +            final String vdiUuid = volume.getPath();
 +            try {
 +                final VDI vdi = VDI.getByUuid(conn, vdiUuid);
 +                vdiMap.put(vdi, volume);
 +            } catch (final Types.UuidInvalid e) {
 +                s_logger.warn("Unable to find vdi by uuid: " + vdiUuid + ", skip it");
 +            }
 +        }
 +        for (final Map.Entry<VDI, VolumeObjectTO> entry : vdiMap.entrySet()) {
 +            final VDI vdi = entry.getKey();
 +            final VolumeObjectTO volumeTO = entry.getValue();
 +            final VBD.Record vbdr = new VBD.Record();
 +            vbdr.VM = vm;
 +            vbdr.VDI = vdi;
 +            if (volumeTO.getVolumeType() == Volume.Type.ROOT) {
 +                vbdr.bootable = true;
 +                vbdr.unpluggable = false;
 +            } else {
 +                vbdr.bootable = false;
 +                vbdr.unpluggable = true;
 +            }
 +            vbdr.userdevice = "autodetect";
 +            vbdr.mode = Types.VbdMode.RW;
 +            vbdr.type = Types.VbdType.DISK;
 +            Long deviceId = volumeTO.getDeviceId();
 +            if (deviceId != null && (!isDeviceUsed(conn, vm, deviceId) || deviceId > 3)) {
 +                vbdr.userdevice = deviceId.toString();
 +            }
 +            VBD.create(conn, vbdr);
 +        }
 +        return vm;
 +    }
 +
 +    protected boolean deleteSecondaryStorageFolder(final Connection conn, final String remoteMountPath, final String folder) {
 +        final String details = callHostPlugin(conn, "vmopsSnapshot", "delete_secondary_storage_folder", "remoteMountPath", remoteMountPath, "folder", folder);
 +        return details != null && details.equals("1");
 +    }
 +
 +    protected String deleteSnapshotBackup(final Connection conn, final Long dcId, final Long accountId, final Long volumeId, final String secondaryStorageMountPath, final String backupUUID) {
 +
 +        // If anybody modifies the formatting below again, I'll skin them
 +        final String result = callHostPlugin(conn, "vmopsSnapshot", "deleteSnapshotBackup", "backupUUID", backupUUID, "dcId", dcId.toString(), "accountId", accountId.toString(), "volumeId",
 +                volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath);
 +
 +        return result;
 +    }
 +
 +    public void destroyPatchVbd(final Connection conn, final String vmName) throws XmlRpcException, XenAPIException {
 +        try {
 +            if (!vmName.startsWith("r-") && !vmName.startsWith("s-") && !vmName.startsWith("v-")) {
 +                return;
 +            }
 +            final Set<VM> vms = VM.getByNameLabel(conn, vmName);
 +            for (final VM vm : vms) {
 +                final Set<VBD> vbds = vm.getVBDs(conn);
 +                for (final VBD vbd : vbds) {
 +                    if (vbd.getType(conn) == Types.VbdType.CD) {
 +                        vbd.eject(conn);
 +                        vbd.destroy(conn);
 +                        break;
 +                    }
 +                }
 +            }
 +        } catch (final Exception e) {
 +            s_logger.debug("Cannot destory CD-ROM device for VM " + vmName + " due to " + e.toString(), e);
 +        }
 +    }
 +
 +    public synchronized void destroyTunnelNetwork(final Connection conn, final Network nw, final long hostId) {
 +        try {
 +            final String bridge = nw.getBridge(conn);
 +            final String result = callHostPlugin(conn, "ovstunnel", "destroy_ovs_bridge", "bridge", bridge, "cs_host_id", ((Long)hostId).toString());
 +            final String[] res = result.split(":");
 +            if (res.length != 2 || !res[0].equalsIgnoreCase("SUCCESS")) {
 +                throw new CloudRuntimeException("Unable to remove OVS bridge " + bridge + ":" + result);
 +            }
 +            return;
 +        } catch (final Exception e) {
 +            s_logger.warn("destroyTunnelNetwork failed:", e);
 +            return;
 +        }
 +    }
 +
 +    void destroyVDIbyNameLabel(final Connection conn, final String nameLabel) {
 +        try {
 +            final Set<VDI> vdis = VDI.getByNameLabel(conn, nameLabel);
 +            if (vdis.size() != 1) {
 +                s_logger.warn("destoryVDIbyNameLabel failed due to there are " + vdis.size() + " VDIs with name " + nameLabel);
 +                return;
 +            }
 +            for (final VDI vdi : vdis) {
 +                try {
 +                    vdi.destroy(conn);
 +                } catch (final Exception e) {
 +                    final String msg = "Failed to destroy VDI : " + nameLabel + "due to " + e.toString() + "\n Force deleting VDI using system 'rm' command";
 +                    s_logger.warn(msg);
 +                    try {
 +                        final String srUUID = vdi.getSR(conn).getUuid(conn);
 +                        final String vdiUUID = vdi.getUuid(conn);
 +                        final String vdifile = "/var/run/sr-mount/" + srUUID + "/" + vdiUUID + ".vhd";
 +                        callHostPluginAsync(conn, "vmopspremium", "remove_corrupt_vdi", 10, "vdifile", vdifile);
 +                    } catch (final Exception e2) {
 +                        s_logger.warn(e2);
 +                    }
 +                }
 +            }
 +        } catch (final Exception e) {
 +        }
 +    }
 +
 +    public void disableVlanNetwork(final Connection conn, final Network network) {
 +    }
 +
 +    @Override
 +    public void disconnected() {
 +    }
 +
 +    public boolean doPingTest(final Connection conn, final String computingHostIp) {
 +        final com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_host.getIp(), 22);
 +        try {
 +            sshConnection.connect(null, 60000, 60000);
 +            if (!sshConnection.authenticateWithPassword(_username, _password.peek())) {
 +                throw new CloudRuntimeException("Unable to authenticate");
 +            }
 +
 +            final String cmd = "ping -c 2 " + computingHostIp;
 +            if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) {
 +                throw new CloudRuntimeException("Cannot ping host " + computingHostIp + " from host " + _host.getIp());
 +            }
 +            return true;
 +        } catch (final Exception e) {
 +            s_logger.warn("Catch exception " + e.toString(), e);
 +            return false;
 +        } finally {
 +            sshConnection.close();
 +        }
 +    }
 +
 +    public boolean doPingTest(final Connection conn, final String domRIp, final String vmIp) {
 +        final String args = "-i " + domRIp + " -p " + vmIp;
 +        final String result = callHostPlugin(conn, "vmops", "pingtest", "args", args);
 +        if (result == null || result.isEmpty()) {
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * enableVlanNetwork creates a Network object, Vlan object, and thereby a
 +     * tagged PIF object in Xapi.
 +     *
 +     * In XenServer, VLAN is added by - Create a network, which is unique
 +     * cluster wide. - Find the PIF that you want to create the VLAN on. -
 +     * Create a VLAN using the network and the PIF. As a result of this
 +     * operation, a tagged PIF object is also created.
 +     *
 +     * Here is a list of problems with clustered Xapi implementation that we are
 +     * trying to circumvent. - There can be multiple Networks with the same
 +     * name-label so searching using name-label is not unique. - There are no
 +     * other ways to search for Networks other than listing all of them which is
 +     * not efficient in our implementation because we can have over 4000 VLAN
 +     * networks. - In a clustered situation, it's possible for both hosts to
 +     * detect that the Network is missing and both creates it. This causes a lot
 +     * of problems as one host may be using one Network and another may be using
 +     * a different network for their VMs. This causes problems in migration
 +     * because the VMs are logically attached to different networks in Xapi's
 +     * database but in reality, they are attached to the same network.
 +     *
 +     * To work around these problems, we do the following.
 +     *
 +     * - When creating the VLAN network, we name it as VLAN-UUID of the Network
 +     * it is created on-VLAN Tag. Because VLAN tags is unique with one
 +     * particular network, this is a unique name-label to quickly retrieve the
 +     * the VLAN network with when we need it again. - When we create the VLAN
 +     * network, we add a timestamp and a random number as a tag into the
 +     * network. Then instead of creating VLAN on that network, we actually
 +     * retrieve the Network again and this time uses the VLAN network with
 +     * lowest timestamp or lowest random number as the VLAN network. This allows
 +     * VLAN creation to happen on multiple hosts concurrently but even if two
 +     * VLAN networks were created with the same name, only one of them is used.
 +     *
 +     * One cavaet about this approach is that it relies on the timestamp to be
 +     * relatively accurate among different hosts.
 +     *
 +     * @param conn
 +     *            Xapi Connection
 +     * @param tag
 +     *            VLAN tag
 +     * @param network
 +     *            network on this host to create the VLAN on.
 +     * @return VLAN Network created.
 +     * @throws XenAPIException
 +     * @throws XmlRpcException
 +     */
 +    protected Network enableVlanNetwork(final Connection conn, final long tag, final XsLocalNetwork network) throws XenAPIException, XmlRpcException {
 +        Network vlanNetwork = null;
 +        final String oldName = "VLAN" + Long.toString(tag);
 +        final String newName = "VLAN-" + network.getNetworkRecord(conn).uuid + "-" + tag;
 +        XsLocalNetwork vlanNic = getNetworkByName(conn, newName);
 +        if (vlanNic == null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Couldn't find vlan network with the new name so trying old name: " + oldName);
 +            }
 +            vlanNic = getNetworkByName(conn, oldName);
 +            if (vlanNic != null) {
 +                s_logger.info("Renaming VLAN with old name " + oldName + " to " + newName);
 +                vlanNic.getNetwork().setNameLabel(conn, newName);
 +            }
 +        }
 +        if (vlanNic == null) { // Can't find it, then create it.
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Creating VLAN network for " + tag + " on host " + _host.getIp());
 +            }
 +            final Network.Record nwr = new Network.Record();
 +            nwr.nameLabel = newName;
 +            nwr.tags = new HashSet<String>();
 +            nwr.tags.add(generateTimeStamp());
 +            vlanNetwork = Network.create(conn, nwr);
 +            vlanNic = getNetworkByName(conn, newName);
 +            if (vlanNic == null) { // Still vlanNic is null means we could not
 +                // create it for some reason and no exception
 +                // capture happened.
 +                throw new CloudRuntimeException("Could not find/create vlan network with name: " + newName);
 +            }
 +        }
 +
 +        final PIF nPif = network.getPif(conn);
 +        final PIF.Record nPifr = network.getPifRecord(conn);
 +
 +        vlanNetwork = vlanNic.getNetwork();
 +        if (vlanNic.getPif(conn) != null) {
 +            return vlanNetwork;
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Creating VLAN " + tag + " on host " + _host.getIp() + " on device " + nPifr.device);
 +        }
 +        final VLAN vlan = VLAN.create(conn, nPif, tag, vlanNetwork);
 +        if (vlan != null) {
 +            final VLAN.Record vlanr = vlan.getRecord(conn);
 +            if (vlanr != null) {
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("VLAN is created for " + tag + ".  The uuid is " + vlanr.uuid);
 +                }
 +            }
 +        }
 +        return vlanNetwork;
 +    }
 +
 +    @Override
 +    public RebootAnswer execute(final RebootCommand cmd) {
 +        throw new CloudRuntimeException("The method has been replaced but the implementation CitrixRebootCommandWrapper. "
 +                + "Please use the new design in order to keep compatibility. Once all ServerResource implementation are refactored those methods will dissapper.");
 +    }
 +
 +    @Override
 +    public StartAnswer execute(final StartCommand cmd) {
 +        throw new CloudRuntimeException("The method has been replaced but the implementation CitrixStartCommandWrapper. "
 +                + "Please use the new design in order to keep compatibility. Once all ServerResource implementation are refactored those methods will dissapper.");
 +    }
 +
 +    @Override
 +    public StopAnswer execute(final StopCommand cmd) {
 +        throw new CloudRuntimeException("The method has been replaced but the implementation CitrixStopCommandWrapper. "
 +                + "Please use the new design in order to keep compatibility. Once all ServerResource implementation are refactored those methods will dissapper.");
 +    }
 +
 +    @Override
 +    public ExecutionResult executeInVR(final String routerIP, final String script, final String args) {
 +        // Timeout is 120 seconds by default
 +        return executeInVR(routerIP, script, args, VRScripts.VR_SCRIPT_EXEC_TIMEOUT);
 +    }
 +
 +    @Override
 +    public ExecutionResult executeInVR(final String routerIP, final String script, final String args, final Duration timeout) {
 +        Pair<Boolean, String> result;
 +        String cmdline = "/opt/cloud/bin/router_proxy.sh " + script + " " + routerIP + " " + args;
 +        // semicolon need to be escape for bash
 +        cmdline = cmdline.replaceAll(";", "\\\\;");
 +        try {
 +            s_logger.debug("Executing command in VR: " + cmdline);
 +            result = SshHelper.sshExecute(_host.getIp(), 22, _username, null, _password.peek(), cmdline, VRScripts.CONNECTION_TIMEOUT, VRScripts.CONNECTION_TIMEOUT, timeout);
 +        } catch (final Exception e) {
 +            return new ExecutionResult(false, e.getMessage());
 +        }
 +        return new ExecutionResult(result.first(), result.second());
 +    }
 +
 +    @Override
 +    public Answer executeRequest(final Command cmd) {
 +        final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
 +        try {
 +            return wrapper.execute(cmd, this);
 +        } catch (final Exception e) {
 +            return Answer.createUnsupportedCommandAnswer(cmd);
 +        }
 +    }
 +
 +    protected void fillHostInfo(final Connection conn, final StartupRoutingCommand cmd) {
 +        final StringBuilder caps = new StringBuilder();
 +        try {
 +
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            final Host.Record hr = host.getRecord(conn);
 +
 +            Map<String, String> details = cmd.getHostDetails();
 +            if (details == null) {
 +                details = new HashMap<String, String>();
 +            }
 +
 +            String productBrand = hr.softwareVersion.get("product_brand");
 +            if (productBrand == null) {
 +                productBrand = hr.softwareVersion.get("platform_name");
 +            }
 +            details.put("product_brand", productBrand);
 +            details.put("product_version", _host.getProductVersion());
 +            if (hr.softwareVersion.get("product_version_text_short") != null) {
 +                details.put("product_version_text_short", hr.softwareVersion.get("product_version_text_short"));
 +                cmd.setHypervisorVersion(hr.softwareVersion.get("product_version_text_short"));
 +
 +                cmd.setHypervisorVersion(_host.getProductVersion());
 +            }
 +            if (_privateNetworkName != null) {
 +                details.put("private.network.device", _privateNetworkName);
 +            }
 +
 +            cmd.setHostDetails(details);
 +            cmd.setName(hr.nameLabel);
 +            cmd.setGuid(_host.getUuid());
 +            cmd.setPool(_host.getPool());
 +            cmd.setDataCenter(Long.toString(_dcId));
 +            for (final String cap : hr.capabilities) {
 +                if (cap.length() > 0) {
 +                    caps.append(cap).append(" , ");
 +                }
 +            }
 +            if (caps.length() > 0) {
 +                caps.delete(caps.length() - 3, caps.length());
 +            }
 +            cmd.setCaps(caps.toString());
 +
 +            cmd.setSpeed(_host.getSpeed());
 +            cmd.setCpuSockets(_host.getCpuSockets());
 +            cmd.setCpus(_host.getCpus());
 +
 +            final HostMetrics hm = host.getMetrics(conn);
 +
 +            long ram = 0;
 +            long dom0Ram = 0;
 +            ram = hm.getMemoryTotal(conn);
 +            final Set<VM> vms = host.getResidentVMs(conn);
 +            for (final VM vm : vms) {
 +                if (vm.getIsControlDomain(conn)) {
 +                    dom0Ram = vm.getMemoryStaticMax(conn);
 +                    break;
 +                }
 +            }
 +
 +            ram = (long)((ram - dom0Ram - _xsMemoryUsed) * _xsVirtualizationFactor);
 +            cmd.setMemory(ram);
 +            cmd.setDom0MinMemory(dom0Ram);
 +
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Total Ram: " + ram + " dom0 Ram: " + dom0Ram);
 +            }
 +
 +            PIF pif = PIF.getByUuid(conn, _host.getPrivatePif());
 +            PIF.Record pifr = pif.getRecord(conn);
 +            if (pifr.IP != null && pifr.IP.length() > 0) {
 +                cmd.setPrivateIpAddress(pifr.IP);
 +                cmd.setPrivateMacAddress(pifr.MAC);
 +                cmd.setPrivateNetmask(pifr.netmask);
 +            } else {
 +                cmd.setPrivateIpAddress(_host.getIp());
 +                cmd.setPrivateMacAddress(pifr.MAC);
 +                cmd.setPrivateNetmask("255.255.255.0");
 +            }
 +
 +            pif = PIF.getByUuid(conn, _host.getPublicPif());
 +            pifr = pif.getRecord(conn);
 +            if (pifr.IP != null && pifr.IP.length() > 0) {
 +                cmd.setPublicIpAddress(pifr.IP);
 +                cmd.setPublicMacAddress(pifr.MAC);
 +                cmd.setPublicNetmask(pifr.netmask);
 +            }
 +
 +            if (_host.getStoragePif1() != null) {
 +                pif = PIF.getByUuid(conn, _host.getStoragePif1());
 +                pifr = pif.getRecord(conn);
 +                if (pifr.IP != null && pifr.IP.length() > 0) {
 +                    cmd.setStorageIpAddress(pifr.IP);
 +                    cmd.setStorageMacAddress(pifr.MAC);
 +                    cmd.setStorageNetmask(pifr.netmask);
 +                }
 +            }
 +
 +            if (_host.getStoragePif2() != null) {
 +                pif = PIF.getByUuid(conn, _host.getStoragePif2());
 +                pifr = pif.getRecord(conn);
 +                if (pifr.IP != null && pifr.IP.length() > 0) {
 +                    cmd.setStorageIpAddressDeux(pifr.IP);
 +                    cmd.setStorageMacAddressDeux(pifr.MAC);
 +                    cmd.setStorageNetmaskDeux(pifr.netmask);
 +                }
 +            }
 +
 +            final Map<String, String> configs = hr.otherConfig;
 +            cmd.setIqn(configs.get("iscsi_iqn"));
 +
 +            cmd.setPod(_pod);
 +            cmd.setVersion(CitrixResourceBase.class.getPackage().getImplementationVersion());
 +
 +            try {
 +                final String cmdLine = "xe sm-list | grep \"resigning of duplicates\"";
 +
 +                final XenServerUtilitiesHelper xenServerUtilitiesHelper = getXenServerUtilitiesHelper();
 +
 +                Pair<Boolean, String> result = xenServerUtilitiesHelper.executeSshWrapper(_host.getIp(), 22, _username, null, getPwdFromQueue(), cmdLine);
 +
 +                boolean supportsClonedVolumes = result != null && result.first() != null && result.first() && result.second() != null && result.second().length() > 0;
 +
 +                cmd.setSupportsClonedVolumes(supportsClonedVolumes);
 +            } catch (NumberFormatException ex) {
 +                s_logger.warn("Issue sending 'xe sm-list' via SSH to XenServer host: " + ex.getMessage());
 +            }
 +        } catch (final XmlRpcException e) {
 +            throw new CloudRuntimeException("XML RPC Exception: " + e.getMessage(), e);
 +        } catch (final XenAPIException e) {
 +            throw new CloudRuntimeException("XenAPIException: " + e.toString(), e);
 +        } catch (final Exception e) {
 +            throw new CloudRuntimeException("Exception: " + e.toString(), e);
 +        }
 +    }
 +
 +    protected void finalizeVmMetaData(final VM vm, final Connection conn, final VirtualMachineTO vmSpec) throws Exception {
 +
 +        final Map<String, String> details = vmSpec.getDetails();
 +        if (details != null) {
 +            final String platformstring = details.get("platform");
 +            if (platformstring != null && !platformstring.isEmpty()) {
 +                final Map<String, String> platform = StringUtils.stringToMap(platformstring);
 +                vm.setPlatform(conn, platform);
 +            } else {
 +                final String timeoffset = details.get("timeoffset");
 +                if (timeoffset != null) {
 +                    final Map<String, String> platform = vm.getPlatform(conn);
 +                    platform.put("timeoffset", timeoffset);
 +                    vm.setPlatform(conn, platform);
 +                }
 +                final String coresPerSocket = details.get("cpu.corespersocket");
 +                if (coresPerSocket != null) {
 +                    final Map<String, String> platform = vm.getPlatform(conn);
 +                    platform.put("cores-per-socket", coresPerSocket);
 +                    vm.setPlatform(conn, platform);
 +                }
 +            }
 +            if (!BootloaderType.CD.equals(vmSpec.getBootloader())) {
 +                final String xenservertoolsversion = details.get("hypervisortoolsversion");
 +                if ((xenservertoolsversion == null || !xenservertoolsversion.equalsIgnoreCase("xenserver61")) && vmSpec.getGpuDevice() == null) {
 +                    final Map<String, String> platform = vm.getPlatform(conn);
 +                    platform.remove("device_id");
 +                    vm.setPlatform(conn, platform);
 +                }
 +            }
 +        }
 +    }
 +
 +    /**
 +     * This method just creates a XenServer network following the tunnel network
 +     * naming convention
 +     */
 +    public synchronized Network findOrCreateTunnelNetwork(final Connection conn, final String nwName) {
 +        try {
 +            Network nw = null;
 +            final Network.Record rec = new Network.Record();
 +            final Set<Network> networks = Network.getByNameLabel(conn, nwName);
 +
 +            if (networks.size() == 0) {
 +                rec.nameDescription = "tunnel network id# " + nwName;
 +                rec.nameLabel = nwName;
 +                // Initialize the ovs-host-setup to avoid error when doing
 +                // get-param in plugin
 +                final Map<String, String> otherConfig = new HashMap<String, String>();
 +                otherConfig.put("ovs-host-setup", "");
 +                // Mark 'internal network' as shared so bridge gets
 +                // automatically created on each host in the cluster
 +                // when VM with vif connected to this internal network is
 +                // started
 +                otherConfig.put("assume_network_is_shared", "true");
 +                rec.otherConfig = otherConfig;
 +                nw = Network.create(conn, rec);
 +                s_logger.debug("### XenServer network for tunnels created:" + nwName);
 +            } else {
 +                nw = networks.iterator().next();
 +                s_logger.debug("XenServer network for tunnels found:" + nwName);
 +            }
 +            return nw;
 +        } catch (final Exception e) {
 +            s_logger.warn("createTunnelNetwork failed", e);
 +            return null;
 +        }
 +    }
 +
 +    void forceShutdownVM(final Connection conn, final VM vm) {
 +        try {
 +            final Long domId = vm.getDomid(conn);
 +            callHostPlugin(conn, "vmopspremium", "forceShutdownVM", "domId", domId.toString());
 +            vm.powerStateReset(conn);
 +            vm.destroy(conn);
 +        } catch (final Exception e) {
 +            final String msg = "forceShutdown failed due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg);
 +        }
 +    }
 +
 +    protected String generateTimeStamp() {
 +        return new StringBuilder("CsCreateTime-").append(System.currentTimeMillis()).append("-").append(Rand.nextInt(Integer.MAX_VALUE)).toString();
 +    }
 +
 +    @Override
 +    public IAgentControl getAgentControl() {
 +        return _agentControl;
 +    }
 +
 +    protected String getArgsString(final Map<String, String> args) {
 +        final StringBuilder argString = new StringBuilder();
 +        for (final Map.Entry<String, String> arg : args.entrySet()) {
 +            argString.append(arg.getKey() + ": " + arg.getValue() + ", ");
 +        }
 +        return argString.toString();
 +    }
 +
 +    @Override
 +    public Map<String, Object> getConfigParams() {
 +        return null;
 +    }
 +
 +    public Connection getConnection() {
 +        return ConnPool.connect(_host.getUuid(), _host.getPool(), _host.getIp(), _username, _password, _wait);
 +    }
 +
 +    protected Pair<VM, VM.Record> getControlDomain(final Connection conn) throws XenAPIException, XmlRpcException {
 +        final Host host = Host.getByUuid(conn, _host.getUuid());
 +        Set<VM> vms = null;
 +        vms = host.getResidentVMs(conn);
 +        for (final VM vm : vms) {
 +            if (vm.getIsControlDomain(conn)) {
 +                return new Pair<VM, VM.Record>(vm, vm.getRecord(conn));
 +            }
 +        }
 +
 +        throw new CloudRuntimeException("Com'on no control domain?  What the crap?!#@!##$@");
 +    }
 +
 +    protected VIF getCorrectVif(final Connection conn, final VM router, final IpAddressTO ip) throws XmlRpcException, XenAPIException {
 +        final NicTO nic = new NicTO();
 +        nic.setType(ip.getTrafficType());
 +        nic.setName(ip.getNetworkName());
 +        if (ip.getBroadcastUri() == null) {
 +            nic.setBroadcastType(BroadcastDomainType.Native);
 +        } else {
 +            final URI uri = BroadcastDomainType.fromString(ip.getBroadcastUri());
 +            nic.setBroadcastType(BroadcastDomainType.getSchemeValue(uri));
 +            nic.setBroadcastUri(uri);
 +        }
 +        final Network network = getNetwork(conn, nic);
 +        // Determine the correct VIF on DomR to associate/disassociate the
 +        // IP address with
 +        final Set<VIF> routerVIFs = router.getVIFs(conn);
 +        for (final VIF vif : routerVIFs) {
 +            final Network vifNetwork = vif.getNetwork(conn);
 +            if (vifNetwork.getUuid(conn).equals(network.getUuid(conn))) {
 +                return vif;
 +            }
 +        }
 +        return null;
 +    }
 +
 +    protected VIF getCorrectVif(final Connection conn, final VM router, final Network network) throws XmlRpcException, XenAPIException {
 +        final Set<VIF> routerVIFs = router.getVIFs(conn);
 +        for (final VIF vif : routerVIFs) {
 +            final Network vifNetwork = vif.getNetwork(conn);
 +            if (vifNetwork.getUuid(conn).equals(network.getUuid(conn))) {
 +                return vif;
 +            }
 +        }
 +
 +        return null;
 +    }
 +
 +    @Override
 +    public PingCommand getCurrentStatus(final long id) {
 +        try {
 +            if (!pingXAPI()) {
 +                Thread.sleep(1000);
 +                if (!pingXAPI()) {
 +                    s_logger.warn("can not ping xenserver " + _host.getUuid());
 +                    return null;
 +                }
 +            }
 +            final Connection conn = getConnection();
 +            if (!_canBridgeFirewall && !_isOvs) {
 +                return new PingRoutingCommand(getType(), id, getHostVmStateReport(conn));
 +            } else if (_isOvs) {
 +                final List<Pair<String, Long>> ovsStates = ovsFullSyncStates();
 +                return new PingRoutingWithOvsCommand(getType(), id, getHostVmStateReport(conn), ovsStates);
 +            } else {
 +                final HashMap<String, Pair<Long, Long>> nwGrpStates = syncNetworkGroups(conn, id);
 +                return new PingRoutingWithNwGroupsCommand(getType(), id, getHostVmStateReport(conn), nwGrpStates);
 +            }
 +        } catch (final Exception e) {
 +            s_logger.warn("Unable to get current status", e);
 +            return null;
 +        }
 +    }
 +
 +    protected double getDataAverage(final Node dataNode, final int col, final int numRows) {
 +        double value = 0;
 +        final double dummy = 0;
 +        int numRowsUsed = 0;
 +        for (int row = 0; row < numRows; row++) {
 +            final Node data = dataNode.getChildNodes().item(numRows - 1 - row).getChildNodes().item(col + 1);
 +            final Double currentDataAsDouble = Double.valueOf(getXMLNodeValue(data));
 +            if (!currentDataAsDouble.equals(Double.NaN)) {
 +                numRowsUsed += 1;
 +                value += currentDataAsDouble;
 +            }
 +        }
 +
 +        if (numRowsUsed == 0) {
 +            if (!Double.isInfinite(value) && !Double.isNaN(value)) {
 +                return value;
 +            } else {
 +                s_logger.warn("Found an invalid value (infinity/NaN) in getDataAverage(), numRows=0");
 +                return dummy;
 +            }
 +        } else {
 +            if (!Double.isInfinite(value / numRowsUsed) && !Double.isNaN(value / numRowsUsed)) {
 +                return value / numRowsUsed;
 +            } else {
 +                s_logger.warn("Found an invalid value (infinity/NaN) in getDataAverage(), numRows>0");
 +                return dummy;
 +            }
 +        }
 +
 +    }
 +
 +    public HashMap<String, HashMap<String, VgpuTypesInfo>> getGPUGroupDetails(final Connection conn) throws XenAPIException, XmlRpcException {
 +        return null;
 +    }
 +
 +    protected String getGuestOsType(String platformEmulator) {
 +        if (org.apache.commons.lang.StringUtils.isBlank(platformEmulator)) {
 +            s_logger.debug("no guest OS type, start it as HVM guest");
 +            platformEmulator = "Other install media";
 +        }
 +        return platformEmulator;
 +    }
 +
 +    public XsHost getHost() {
 +        return _host;
 +    }
 +
 +    public int getMigrateWait() {
 +        return _migratewait;
 +    }
 +
 +    public StorageSubsystemCommandHandler getStorageHandler() {
 +        return storageHandler;
 +    }
 +
 +    protected boolean getHostInfo(final Connection conn) throws IllegalArgumentException {
 +        try {
 +            final Host myself = Host.getByUuid(conn, _host.getUuid());
 +            Set<HostCpu> hcs = null;
 +            for (int i = 0; i < 10; i++) {
 +                hcs = myself.getHostCPUs(conn);
 +                if (hcs != null) {
 +                    _host.setCpus(hcs.size());
 +                    if (_host.getCpus() > 0) {
 +                        break;
 +                    }
 +                }
 +                Thread.sleep(5000);
 +            }
 +            if (_host.getCpus() <= 0) {
 +                throw new CloudRuntimeException("Cannot get the numbers of cpu from XenServer host " + _host.getIp());
 +            }
 +            final Map<String, String> cpuInfo = myself.getCpuInfo(conn);
 +            if (cpuInfo.get("socket_count") != null) {
 +                _host.setCpuSockets(Integer.parseInt(cpuInfo.get("socket_count")));
 +            }
 +            // would hcs be null we would have thrown an exception on condition
 +            // (_host.getCpus() <= 0) by now
 +            for (final HostCpu hc : hcs) {
 +                _host.setSpeed(hc.getSpeed(conn).intValue());
 +                break;
 +            }
 +            final Host.Record hr = myself.getRecord(conn);
 +            _host.setProductVersion(CitrixHelper.getProductVersion(hr));
 +
 +            final XsLocalNetwork privateNic = getManagementNetwork(conn);
 +            _privateNetworkName = privateNic.getNetworkRecord(conn).nameLabel;
 +            _host.setPrivatePif(privateNic.getPifRecord(conn).uuid);
 +            _host.setPrivateNetwork(privateNic.getNetworkRecord(conn).uuid);
 +            _host.setSystemvmisouuid(null);
 +
 +            XsLocalNetwork guestNic = null;
 +            if (_guestNetworkName != null && !_guestNetworkName.equals(_privateNetworkName)) {
 +                guestNic = getNetworkByName(conn, _guestNetworkName);
 +                if (guestNic == null) {
 +                    s_logger.warn("Unable to find guest network " + _guestNetworkName);
 +                    throw new IllegalArgumentException("Unable to find guest network " + _guestNetworkName + " for host " + _host.getIp());
 +                }
 +            } else {
 +                guestNic = privateNic;
 +                _guestNetworkName = _privateNetworkName;
 +            }
 +            _host.setGuestNetwork(guestNic.getNetworkRecord(conn).uuid);
 +            _host.setGuestPif(guestNic.getPifRecord(conn).uuid);
 +
 +            XsLocalNetwork publicNic = null;
 +            if (_publicNetworkName != null && !_publicNetworkName.equals(_guestNetworkName)) {
 +                publicNic = getNetworkByName(conn, _publicNetworkName);
 +                if (publicNic == null) {
 +                    s_logger.warn("Unable to find public network " + _publicNetworkName + " for host " + _host.getIp());
 +                    throw new IllegalArgumentException("Unable to find public network " + _publicNetworkName + " for host " + _host.getIp());
 +                }
 +            } else {
 +                publicNic = guestNic;
 +                _publicNetworkName = _guestNetworkName;
 +            }
 +            _host.setPublicPif(publicNic.getPifRecord(conn).uuid);
 +            _host.setPublicNetwork(publicNic.getNetworkRecord(conn).uuid);
 +            if (_storageNetworkName1 == null) {
 +                _storageNetworkName1 = _guestNetworkName;
 +            }
 +            XsLocalNetwork storageNic1 = null;
 +            storageNic1 = getNetworkByName(conn, _storageNetworkName1);
 +            if (storageNic1 == null) {
 +                s_logger.warn("Unable to find storage network " + _storageNetworkName1 + " for host " + _host.getIp());
 +                throw new IllegalArgumentException("Unable to find storage network " + _storageNetworkName1 + " for host " + _host.getIp());
 +            } else {
 +                _host.setStorageNetwork1(storageNic1.getNetworkRecord(conn).uuid);
 +                _host.setStoragePif1(storageNic1.getPifRecord(conn).uuid);
 +            }
 +
 +            XsLocalNetwork storageNic2 = null;
 +            if (_storageNetworkName2 != null) {
 +                storageNic2 = getNetworkByName(conn, _storageNetworkName2);
 +                if (storageNic2 != null) {
 +                    _host.setStoragePif2(storageNic2.getPifRecord(conn).uuid);
 +                }
 +            }
 +
 +            s_logger.info("XenServer Version is " + _host.getProductVersion() + " for host " + _host.getIp());
 +            s_logger.info("Private Network is " + _privateNetworkName + " for host " + _host.getIp());
 +            s_logger.info("Guest Network is " + _guestNetworkName + " for host " + _host.getIp());
 +            s_logger.info("Public Network is " + _publicNetworkName + " for host " + _host.getIp());
 +
 +            return true;
 +        } catch (final XenAPIException e) {
 +            s_logger.warn("Unable to get host information for " + _host.getIp(), e);
 +            return false;
 +        } catch (final Exception e) {
 +            s_logger.warn("Unable to get host information for " + _host.getIp(), e);
 +            return false;
 +        }
 +    }
 +
 +    public HostStatsEntry getHostStats(final Connection conn, final GetHostStatsCommand cmd, final String hostGuid, final long hostId) {
 +
 +        final HostStatsEntry hostStats = new HostStatsEntry(hostId, 0, 0, 0, "host", 0, 0, 0, 0);
 +        final Object[] rrdData = getRRDData(conn, 1); // call rrd method with 1
 +        // for host
 +
 +        if (rrdData == null) {
 +            return null;
 +        }
 +
 +        final Integer numRows = (Integer)rrdData[0];
 +        final Integer numColumns = (Integer)rrdData[1];
 +        final Node legend = (Node)rrdData[2];
 +        final Node dataNode = (Node)rrdData[3];
 +
 +        final NodeList legendChildren = legend.getChildNodes();
 +        for (int col = 0; col < numColumns; col++) {
 +
 +            if (legendChildren == null || legendChildren.item(col) == null) {
 +                continue;
 +            }
 +
 +            final String columnMetadata = getXMLNodeValue(legendChildren.item(col));
 +
 +            if (columnMetadata == null) {
 +                continue;
 +            }
 +
 +            final String[] columnMetadataList = columnMetadata.split(":");
 +
 +            if (columnMetadataList.length != 4) {
 +                continue;
 +            }
 +
 +            final String type = columnMetadataList[1];
 +            final String param = columnMetadataList[3];
 +
 +            if (type.equalsIgnoreCase("host")) {
 +
 +                if (param.matches("pif_eth0_rx")) {
 +                    hostStats.setNetworkReadKBs(getDataAverage(dataNode, col, numRows) / 1000);
 +                } else if (param.matches("pif_eth0_tx")) {
 +                    hostStats.setNetworkWriteKBs(getDataAverage(dataNode, col, numRows) / 1000);
 +                } else if (param.contains("memory_total_kib")) {
 +                    hostStats.setTotalMemoryKBs(getDataAverage(dataNode, col, numRows));
 +                } else if (param.contains("memory_free_kib")) {
 +                    hostStats.setFreeMemoryKBs(getDataAverage(dataNode, col, numRows));
 +                } else if (param.matches("cpu_avg")) {
 +                    // hostStats.setNumCpus(hostStats.getNumCpus() + 1);
 +                    hostStats.setCpuUtilization(hostStats.getCpuUtilization() + getDataAverage(dataNode, col, numRows));
 +                }
 +
 +                /*
 +                 * if (param.contains("loadavg")) {
 +                 * hostStats.setAverageLoad((hostStats.getAverageLoad() +
 +                 * getDataAverage(dataNode, col, numRows))); }
 +                 */
 +            }
 +        }
 +
 +        // add the host cpu utilization
 +        /*
 +         * if (hostStats.getNumCpus() != 0) {
 +         * hostStats.setCpuUtilization(hostStats.getCpuUtilization() /
 +         * hostStats.getNumCpus()); s_logger.debug("Host cpu utilization " +
 +         * hostStats.getCpuUtilization()); }
 +         */
 +
 +        return hostStats;
 +    }
 +
 +    protected HashMap<String, HostVmStateReportEntry> getHostVmStateReport(final Connection conn) {
 +
 +        // TODO : new VM sync model does not require a cluster-scope report, we
 +        // need to optimize
 +        // the report accordingly
 +        final HashMap<String, HostVmStateReportEntry> vmStates = new HashMap<String, HostVmStateReportEntry>();
 +        Map<VM, VM.Record> vm_map = null;
 +        for (int i = 0; i < 2; i++) {
 +            try {
 +                vm_map = VM.getAllRecords(conn); // USE THIS TO GET ALL VMS FROM
 +                // A CLUSTER
 +                break;
 +            } catch (final Throwable e) {
 +                s_logger.warn("Unable to get vms", e);
 +            }
 +            try {
 +                Thread.sleep(1000);
 +            } catch (final InterruptedException ex) {
 +
 +            }
 +        }
 +
 +        if (vm_map == null) {
 +            return vmStates;
 +        }
 +        for (final VM.Record record : vm_map.values()) {
 +            if (record.isControlDomain || record.isASnapshot || record.isATemplate) {
 +                continue; // Skip DOM0
 +            }
 +
 +            final VmPowerState ps = record.powerState;
 +            final Host host = record.residentOn;
 +            String host_uuid = null;
 +            if (!isRefNull(host)) {
 +                try {
 +                    host_uuid = host.getUuid(conn);
 +                } catch (final BadServerResponse e) {
 +                    s_logger.error("Failed to get host uuid for host " + host.toWireString(), e);
 +                } catch (final XenAPIException e) {
 +                    s_logger.error("Failed to get host uuid for host " + host.toWireString(), e);
 +                } catch (final XmlRpcException e) {
 +                    s_logger.error("Failed to get host uuid for host " + host.toWireString(), e);
 +                }
 +
 +                if (host_uuid.equalsIgnoreCase(_host.getUuid())) {
 +                    vmStates.put(record.nameLabel, new HostVmStateReportEntry(convertToPowerState(ps), host_uuid));
 +                }
 +            }
 +        }
 +
 +        return vmStates;
 +    }
 +
 +    public SR getIscsiSR(final Connection conn, final String srNameLabel, final String target, String path, final String chapInitiatorUsername, final String chapInitiatorPassword,
 +            final boolean ignoreIntroduceException) {
 +
 +        return getIscsiSR(conn, srNameLabel, target, path, chapInitiatorUsername, chapInitiatorPassword, false, SRType.LVMOISCSI.toString(), ignoreIntroduceException);
 +    }
 +
 +    public SR getIscsiSR(final Connection conn, final String srNameLabel, final String target, String path, final String chapInitiatorUsername, final String chapInitiatorPassword,
 +            final boolean resignature, final boolean ignoreIntroduceException) {
 +
 +        return getIscsiSR(conn, srNameLabel, target, path, chapInitiatorUsername, chapInitiatorPassword, resignature, SRType.LVMOISCSI.toString(), ignoreIntroduceException);
 +    }
 +
 +    public SR getIscsiSR(final Connection conn, final String srNameLabel, final String target, String path, final String chapInitiatorUsername, final String chapInitiatorPassword,
 +            final boolean resignature, final String srType, final boolean ignoreIntroduceException) {
 +        synchronized (srNameLabel.intern()) {
 +            final Map<String, String> deviceConfig = new HashMap<String, String>();
 +            try {
 +                if (path.endsWith("/")) {
 +                    path = path.substring(0, path.length() - 1);
 +                }
 +
 +                final String tmp[] = path.split("/");
 +                if (tmp.length != 3) {
 +                    final String msg = "Wrong iscsi path " + path + " it should be /targetIQN/LUN";
 +                    s_logger.warn(msg);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +                final String targetiqn = tmp[1].trim();
 +                final String lunid = tmp[2].trim();
 +                String scsiid = "";
 +
 +                //Throws an exception if SR already exists and is attached
 +                checkIfIscsiSrExisits(conn, srNameLabel, target, targetiqn, lunid);
 +
 +                // We now know the SR is not attached to the XenServer. We probe the
 +                // LUN to see if an SR was already exists on it, if so, we just
 +                // attach it or else we create a brand new SR
 +
 +                deviceConfig.put("target", target);
 +                deviceConfig.put("targetIQN", targetiqn);
 +
 +                if (StringUtils.isNotBlank(chapInitiatorUsername) && StringUtils.isNotBlank(chapInitiatorPassword)) {
 +                    deviceConfig.put("chapuser", chapInitiatorUsername);
 +                    deviceConfig.put("chappassword", chapInitiatorPassword);
 +                }
 +
 +                final Host host = Host.getByUuid(conn, _host.getUuid());
 +                final Map<String, String> smConfig = new HashMap<String, String>();
 +                SR sr = null;
 +                String pooluuid = null;
 +
 +                if (SRType.LVMOISCSI.equals(srType)) {
 +                    scsiid = probeScisiId(conn, host, deviceConfig, srType, srNameLabel, lunid, smConfig);
 +                    deviceConfig.put("SCSIid", scsiid);
 +
 +                    String result = SR.probe(conn, host, deviceConfig, srType, smConfig);
 +                    if (result.indexOf("<UUID>") != -1) {
 +                        pooluuid = result.substring(result.indexOf("<UUID>") + 6, result.indexOf("</UUID>")).trim();
 +                    }
 +                }
 +
 +                if (pooluuid == null || pooluuid.length() != 36) {
 +                    sr = SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, srType, "user", true, smConfig);
 +                } else {
 +                    if (resignature) {
 +                        // We resignature the SR for managed storage if needed. At the end of this
 +                        // we have an SR which is ready to be attached. For VHDoISCSI SR,
 +                        // we don't need to resignature
 +                        pooluuid = resignatureIscsiSr(conn, host, deviceConfig, srNameLabel, smConfig);
 +                    }
 +                    sr = introduceAndPlugIscsiSr(conn, pooluuid, srNameLabel, srType, smConfig, deviceConfig, ignoreIntroduceException);
 +                }
 +
 +                sr.scan(conn);
 +                return sr;
 +
 +            } catch (final XenAPIException e) {
 +                final String msg = "Unable to create Iscsi SR  " + deviceConfig + " due to  " + e.toString();
 +                s_logger.warn(msg, e);
 +                throw new CloudRuntimeException(msg, e);
 +            } catch (final Exception e) {
 +                final String msg = "Unable to create Iscsi SR  " + deviceConfig + " due to  " + e.getMessage();
 +                s_logger.warn(msg, e);
 +                throw new CloudRuntimeException(msg, e);
 +            }
 +        }
 +    }
 +
 +    private SR introduceAndPlugIscsiSr(Connection conn, String pooluuid, String srNameLabel, String type, Map<String, String> smConfig, Map<String, String> deviceConfig,
 +            boolean ignoreIntroduceException) throws XmlRpcException, XenAPIException {
 +        SR sr = null;
 +        try {
 +            sr = SR.introduce(conn, pooluuid, srNameLabel, srNameLabel, type, "user", true, smConfig);
 +        } catch (final XenAPIException ex) {
 +            if (ignoreIntroduceException) {
 +                return sr;
 +            }
 +
 +            throw ex;
 +        }
 +
 +        final Set<Host> setHosts = Host.getAll(conn);
 +
 +        if (setHosts == null) {
 +            final String msg = "Unable to create iSCSI SR " + deviceConfig + " due to hosts not available.";
 +
 +            s_logger.warn(msg);
 +
 +            throw new CloudRuntimeException(msg);
 +        }
 +
 +        for (final Host currentHost : setHosts) {
 +            final PBD.Record rec = new PBD.Record();
 +
 +            rec.deviceConfig = deviceConfig;
 +            rec.host = currentHost;
 +            rec.SR = sr;
 +
 +            final PBD pbd = PBD.create(conn, rec);
 +
 +            pbd.plug(conn);
 +        }
 +
 +        return sr;
 +    }
 +
 +    private String resignatureIscsiSr(Connection conn, Host host, Map<String, String> deviceConfig, String srNameLabel, Map<String, String> smConfig) throws XmlRpcException, XenAPIException {
 +        String pooluuid;
 +
 +        try {
 +            SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, SRType.RELVMOISCSI.toString(), "user", true, smConfig);
 +
 +            // The successful outcome of SR.create (right above) is to throw an exception of type XenAPIException (with expected
 +            // toString() text) after resigning the metadata (we indicated to perform a resign by passing in SRType.RELVMOISCSI.toString()).
 +            // That being the case, if this CloudRuntimeException statement is executed, there appears to have been some kind
 +            // of failure in the execution of the above SR.create (resign) method.
 +            throw new CloudRuntimeException("Problem resigning the metadata");
 +        } catch (XenAPIException ex) {
 +            String msg = ex.toString();
 +
 +            if (!msg.contains("successfully resigned")) {
 +                throw ex;
 +            }
 +
 +            String type = SRType.LVMOISCSI.toString();
 +            String result = SR.probe(conn, host, deviceConfig, type, smConfig);
 +
 +            pooluuid = null;
 +
 +            if (result.indexOf("<UUID>") != -1) {
 +                pooluuid = result.substring(result.indexOf("<UUID>") + 6, result.indexOf("</UUID>")).trim();
 +            }
 +
 +            if (pooluuid == null || pooluuid.length() != 36) {
 +                throw new CloudRuntimeException("Non-existent or invalid SR UUID");
 +            }
 +        }
 +
 +        return pooluuid;
 +    }
 +
 +    private void checkIfIscsiSrExisits(Connection conn, String srNameLabel, String target, String targetiqn, String lunid) throws XenAPIException, XmlRpcException {
 +        final Set<SR> srs = SR.getByNameLabel(conn, srNameLabel);
 +        for (final SR sr : srs) {
 +            if (!(SRType.LVMOISCSI.equals(sr.getType(conn)))) {
 +                continue;
 +            }
 +            final Set<PBD> pbds = sr.getPBDs(conn);
 +            if (pbds.isEmpty()) {
 +                continue;
 +            }
 +            final PBD pbd = pbds.iterator().next();
 +            final Map<String, String> dc = pbd.getDeviceConfig(conn);
 +            if (dc == null) {
 +                continue;
 +            }
 +            if (dc.get("target") == null) {
 +                continue;
 +            }
 +            if (dc.get("targetIQN") == null) {
 +                continue;
 +            }
 +            if (dc.get("lunid") == null) {
 +                continue;
 +            }
 +            if (target.equals(dc.get("target")) && targetiqn.equals(dc.get("targetIQN")) && lunid.equals(dc.get("lunid"))) {
 +                throw new CloudRuntimeException("There is a SR using the same configuration target:" + dc.get("target") + ",  targetIQN:" + dc.get("targetIQN") + ", lunid:" + dc.get("lunid")
 +                + " for pool " + srNameLabel + "on host:" + _host.getUuid());
 +            }
 +        }
 +
 +    }
 +
 +    private String probeScisiId(Connection conn, Host host, Map<String, String> deviceConfig, String type, String srNameLabel, String lunid, Map<String, String> smConfig)
 +            throws XenAPIException, XmlRpcException {
 +        String scsiid = null;
 +
 +        try {
 +            SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, type, "user", true, smConfig);
 +        } catch (final XenAPIException e) {
 +            final String errmsg = e.toString();
 +            if (errmsg.contains("SR_BACKEND_FAILURE_107")) {
 +                final String lun[] = errmsg.split("<LUN>");
 +                boolean found = false;
 +                for (int i = 1; i < lun.length; i++) {
 +                    final int blunindex = lun[i].indexOf("<LUNid>") + 7;
 +                    final int elunindex = lun[i].indexOf("</LUNid>");
 +                    String ilun = lun[i].substring(blunindex, elunindex);
 +                    ilun = ilun.trim();
 +                    if (ilun.equals(lunid)) {
 +                        final int bscsiindex = lun[i].indexOf("<SCSIid>") + 8;
 +                        final int escsiindex = lun[i].indexOf("</SCSIid>");
 +                        scsiid = lun[i].substring(bscsiindex, escsiindex);
 +                        scsiid = scsiid.trim();
 +                        found = true;
 +                        break;
 +                    }
 +                }
 +                if (!found) {
 +                    final String msg = "can not find LUN " + lunid + " in " + errmsg;
 +                    s_logger.warn(msg);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +            } else {
 +                final String msg = "Unable to create Iscsi SR  " + deviceConfig + " due to  " + e.toString();
 +                s_logger.warn(msg, e);
 +                throw new CloudRuntimeException(msg, e);
 +            }
 +        }
 +        return scsiid;
 +    }
 +
 +    public SR getISOSRbyVmName(final Connection conn, final String vmName) {
 +        try {
 +            final Set<SR> srs = SR.getByNameLabel(conn, vmName + "-ISO");
 +            if (srs.size() == 0) {
 +                return null;
 +            } else if (srs.size() == 1) {
 +                return srs.iterator().next();
 +            } else {
 +                final String msg = "getIsoSRbyVmName failed due to there are more than 1 SR having same Label";
 +                s_logger.warn(msg);
 +            }
 +        } catch (final XenAPIException e) {
 +            final String msg = "getIsoSRbyVmName failed due to " + e.toString();
 +            s_logger.warn(msg, e);
 +        } catch (final Exception e) {
 +            final String msg = "getIsoSRbyVmName failed due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +        }
 +        return null;
 +    }
 +
 +    public VDI getIsoVDIByURL(final Connection conn, final String vmName, final String isoURL) {
 +        SR isoSR = null;
 +        String mountpoint = null;
 +        if (isoURL.startsWith("xs-tools")) {
 +            try {
 +                final String actualIsoURL = getActualIsoTemplate(conn);
 +                final Set<VDI> vdis = VDI.getByNameLabel(conn, actualIsoURL);
 +                if (vdis.isEmpty()) {
 +                    throw new CloudRuntimeException("Could not find ISO with URL: " + actualIsoURL);
 +                }
 +                return vdis.iterator().next();
 +
 +            } catch (final XenAPIException e) {
 +                throw new CloudRuntimeException("Unable to get pv iso: " + isoURL + " due to " + e.toString());
 +            } catch (final Exception e) {
 +                throw new CloudRuntimeException("Unable to get pv iso: " + isoURL + " due to " + e.toString());
 +            }
 +        }
 +
 +        final int index = isoURL.lastIndexOf("/");
 +        mountpoint = isoURL.substring(0, index);
 +
 +        URI uri;
 +        try {
 +            uri = new URI(mountpoint);
 +        } catch (final URISyntaxException e) {
 +            throw new CloudRuntimeException("isoURL is wrong: " + isoURL);
 +        }
 +        isoSR = getISOSRbyVmName(conn, vmName);
 +        if (isoSR == null) {
 +            isoSR = createIsoSRbyURI(conn, uri, vmName, false);
 +        }
 +
 +        final String isoName = isoURL.substring(index + 1);
 +
 +        final VDI isoVDI = getVDIbyLocationandSR(conn, isoName, isoSR);
 +
 +        if (isoVDI != null) {
 +            return isoVDI;
 +        } else {
 +            throw new CloudRuntimeException("Could not find ISO with URL: " + isoURL);
 +        }
 +    }
 +
 +    /**
 +     * Retrieve the actual ISO 'name-label' to be used.
 +     * We based our decision on XenServer version.
 +     * <ul>
 +     *  <li> for XenServer 7.0+, we use {@value #xenServer70plusGuestToolsName};
 +     *  <li> for versions before 7.0, we use {@value #xenServerBefore70GuestToolsName}.
 +     * </ul>
 +     *
 +     * For XCP we always use {@value #xenServerBefore70GuestToolsName}.
 +     */
 +    protected String getActualIsoTemplate(Connection conn) throws XenAPIException, XmlRpcException {
 +        Host host = Host.getByUuid(conn, _host.getUuid());
 +        Host.Record record = host.getRecord(conn);
 +        String xenBrand = record.softwareVersion.get("product_brand");
 +        String xenVersion = record.softwareVersion.get("product_version");
 +        String[] items = xenVersion.split("\\.");
 +
 +        if (xenBrand.equals("XenServer") && Integer.parseInt(items[0]) >= 7) {
 +            return xenServer70plusGuestToolsName;
 +        }
 +        return xenServerBefore70GuestToolsName;
 +    }
 +
 +    public String getLabel() {
 +        final Connection conn = getConnection();
 +        final String result = callHostPlugin(conn, "ovstunnel", "getLabel");
 +        return result;
 +    }
 +
 +    protected SR getLocalEXTSR(final Connection conn) {
 +        try {
 +            final Map<SR, SR.Record> map = SR.getAllRecords(conn);
 +            if (map != null && !map.isEmpty()) {
 +                for (final Map.Entry<SR, SR.Record> entry : map.entrySet()) {
 +                    final SR.Record srRec = entry.getValue();
 +                    if (SRType.FILE.equals(srRec.type) || SRType.EXT.equals(srRec.type)) {
 +                        final Set<PBD> pbds = srRec.PBDs;
 +                        if (pbds == null) {
 +                            continue;
 +                        }
 +                        for (final PBD pbd : pbds) {
 +                            final Host host = pbd.getHost(conn);
 +                            if (!isRefNull(host) && host.getUuid(conn).equals(_host.getUuid())) {
 +                                if (!pbd.getCurrentlyAttached(conn)) {
 +                                    pbd.plug(conn);
 +                                }
 +                                final SR sr = entry.getKey();
 +                                sr.scan(conn);
 +                                return sr;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        } catch (final XenAPIException e) {
 +            final String msg = "Unable to get local EXTSR in host:" + _host.getUuid() + e.toString();
 +            s_logger.warn(msg);
 +        } catch (final XmlRpcException e) {
 +            final String msg = "Unable to get local EXTSR in host:" + _host.getUuid() + e.getCause();
 +            s_logger.warn(msg);
 +        }
 +        return null;
 +    }
 +
 +    protected SR getLocalLVMSR(final Connection conn) {
 +        try {
 +            final Map<SR, SR.Record> map = SR.getAllRecords(conn);
 +            if (map != null && !map.isEmpty()) {
 +                for (final Map.Entry<SR, SR.Record> entry : map.entrySet()) {
 +                    final SR.Record srRec = entry.getValue();
 +                    if (SRType.LVM.equals(srRec.type)) {
 +                        final Set<PBD> pbds = srRec.PBDs;
 +                        if (pbds == null) {
 +                            continue;
 +                        }
 +                        for (final PBD pbd : pbds) {
 +                            final Host host = pbd.getHost(conn);
 +                            if (!isRefNull(host) && host.getUuid(conn).equals(_host.getUuid())) {
 +                                if (!pbd.getCurrentlyAttached(conn)) {
 +                                    pbd.plug(conn);
 +                                }
 +                                final SR sr = entry.getKey();
 +                                sr.scan(conn);
 +                                return sr;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        } catch (final XenAPIException e) {
 +            final String msg = "Unable to get local LVMSR in host:" + _host.getUuid() + e.toString();
 +            s_logger.warn(msg);
 +        } catch (final XmlRpcException e) {
 +            final String msg = "Unable to get local LVMSR in host:" + _host.getUuid() + e.getCause();
 +            s_logger.warn(msg);
 +        }
 +        return null;
 +    }
 +
 +    public String getLowestAvailableVIFDeviceNum(final Connection conn, final VM vm) {
 +        String vmName = "";
 +        try {
 +            vmName = vm.getNameLabel(conn);
 +            final List<Integer> usedDeviceNums = new ArrayList<Integer>();
 +            final Set<VIF> vifs = vm.getVIFs(conn);
 +            final Iterator<VIF> vifIter = vifs.iterator();
 +            while (vifIter.hasNext()) {
 +                final VIF vif = vifIter.next();
 +                try {
 +                    final String deviceId = vif.getDevice(conn);
 +                    if (vm.getIsControlDomain(conn) || vif.getCurrentlyAttached(conn)) {
 +                        usedDeviceNums.add(Integer.valueOf(deviceId));
 +                    } else {
 +                        s_logger.debug("Found unplugged VIF " + deviceId + " in VM " + vmName + " destroy it");
 +                        vif.destroy(conn);
 +                    }
 +                } catch (final NumberFormatException e) {
 +                    final String msg = "Obtained an invalid value for an allocated VIF device number for VM: " + vmName;
 +                    s_logger.debug(msg, e);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +            }
 +
 +            for (Integer i = 0; i < _maxNics; i++) {
 +                if (!usedDeviceNums.contains(i)) {
 +                    s_logger.debug("Lowest available Vif device number: " + i + " for VM: " + vmName);
 +                    return i.toString();
 +                }
 +            }
 +        } catch (final XmlRpcException e) {
 +            final String msg = "Caught XmlRpcException: " + e.getMessage();
 +            s_logger.warn(msg, e);
 +        } catch (final XenAPIException e) {
 +            final String msg = "Caught XenAPIException: " + e.toString();
 +            s_logger.warn(msg, e);
 +        }
 +
 +        throw new CloudRuntimeException("Could not find available VIF slot in VM with name: " + vmName);
 +    }
 +
 +    protected XsLocalNetwork getManagementNetwork(final Connection conn) throws XmlRpcException, XenAPIException {
 +        PIF mgmtPif = null;
 +        PIF.Record mgmtPifRec = null;
 +        final Host host = Host.getByUuid(conn, _host.getUuid());
 +        final Set<PIF> hostPifs = host.getPIFs(conn);
 +        for (final PIF pif : hostPifs) {
 +            final PIF.Record rec = pif.getRecord(conn);
 +            if (rec.management) {
 +                if (rec.VLAN != null && rec.VLAN != -1) {
 +                    final String msg = new StringBuilder("Unsupported configuration.  Management network is on a VLAN.  host=").append(_host.getUuid()).append("; pif=").append(rec.uuid)
 +                            .append("; vlan=").append(rec.VLAN).toString();
 +                    s_logger.warn(msg);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug("Management network is on pif=" + rec.uuid);
 +                }
 +                mgmtPif = pif;
 +                mgmtPifRec = rec;
 +                break;
 +            }
 +        }
 +        if (mgmtPif == null) {
 +            final String msg = "Unable to find management network for " + _host.getUuid();
 +            s_logger.warn(msg);
 +            throw new CloudRuntimeException(msg);
 +        }
 +        final Bond bond = mgmtPifRec.bondSlaveOf;
 +        if (!isRefNull(bond)) {
 +            final String msg = "Management interface is on slave(" + mgmtPifRec.uuid + ") of bond(" + bond.getUuid(conn) + ") on host(" + _host.getUuid()
 +            + "), please move management interface to bond!";
 +            s_logger.warn(msg);
 +            throw new CloudRuntimeException(msg);
 +        }
 +        final Network nk = mgmtPifRec.network;
 +        final Network.Record nkRec = nk.getRecord(conn);
 +        return new XsLocalNetwork(this, nk, nkRec, mgmtPif, mgmtPifRec);
 +    }
 +
 +    @Override
 +    public String getName() {
 +        return _name;
 +    }
 +
 +    public XsLocalNetwork getNativeNetworkForTraffic(final Connection conn, final TrafficType type, final String name) throws XenAPIException, XmlRpcException {
 +        if (name != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Looking for network named " + name);
 +            }
 +            return getNetworkByName(conn, name);
 +        }
 +
 +        if (type == TrafficType.Guest) {
 +            return new XsLocalNetwork(this, Network.getByUuid(conn, _host.getGuestNetwork()), null, PIF.getByUuid(conn, _host.getGuestPif()), null);
 +        } else if (type == TrafficType.Control) {
 +            setupLinkLocalNetwork(conn);
 +            return new XsLocalNetwork(this, Network.getByUuid(conn, _host.getLinkLocalNetwork()));
 +        } else if (type == TrafficType.Management) {
 +            return new XsLocalNetwork(this, Network.getByUuid(conn, _host.getPrivateNetwork()), null, PIF.getByUuid(conn, _host.getPrivatePif()), null);
 +        } else if (type == TrafficType.Public) {
 +            return new XsLocalNetwork(this, Network.getByUuid(conn, _host.getPublicNetwork()), null, PIF.getByUuid(conn, _host.getPublicPif()), null);
 +        } else if (type == TrafficType.Storage) {
 +            /*
 +             * TrafficType.Storage is for secondary storage, while
 +             * storageNetwork1 is for primary storage, we need better name here
 +             */
 +            return new XsLocalNetwork(this, Network.getByUuid(conn, _host.getStorageNetwork1()), null, PIF.getByUuid(conn, _host.getStoragePif1()), null);
 +        }
 +
 +        throw new CloudRuntimeException("Unsupported network type: " + type);
 +    }
 +
 +    public Network getNetwork(final Connection conn, final NicTO nic) throws XenAPIException, XmlRpcException {
 +        final String name = nic.getName();
 +        final XsLocalNetwork network = getNativeNetworkForTraffic(conn, nic.getType(), name);
 +        if (network == null) {
 +            s_logger.error("Network is not configured on the backend for nic " + nic.toString());
 +            throw new CloudRuntimeException("Network for the backend is not configured correctly for network broadcast domain: " + nic.getBroadcastUri());
 +        }
 +        final URI uri = nic.getBroadcastUri();
 +        final BroadcastDomainType type = nic.getBroadcastType();
 +        if (uri != null && uri.toString().contains("untagged")) {
 +            return network.getNetwork();
 +        } else if (uri != null && type == BroadcastDomainType.Vlan) {
 +            assert BroadcastDomainType.getSchemeValue(uri) == BroadcastDomainType.Vlan;
 +            final long vlan = Long.parseLong(BroadcastDomainType.getValue(uri));
 +            return enableVlanNetwork(conn, vlan, network);
 +        } else if (type == BroadcastDomainType.Native || type == BroadcastDomainType.LinkLocal || type == BroadcastDomainType.Vsp) {
 +            return network.getNetwork();
 +        } else if (uri != null && type == BroadcastDomainType.Vswitch) {
 +            final String header = uri.toString().substring(Networks.BroadcastDomainType.Vswitch.scheme().length() + "://".length());
 +            if (header.startsWith("vlan")) {
 +                _isOvs = true;
 +                return setupvSwitchNetwork(conn);
 +            } else {
 +                return findOrCreateTunnelNetwork(conn, getOvsTunnelNetworkName(uri.getAuthority()));
 +            }
 +        } else if (type == BroadcastDomainType.Storage) {
 +            if (uri == null) {
 +                return network.getNetwork();
 +            } else {
 +                final long vlan = Long.parseLong(BroadcastDomainType.getValue(uri));
 +                return enableVlanNetwork(conn, vlan, network);
 +            }
 +        } else if (type == BroadcastDomainType.Lswitch) {
 +            // Nicira Logical Switch
 +            return network.getNetwork();
 +        } else if (uri != null && type == BroadcastDomainType.Pvlan) {
 +            assert BroadcastDomainType.getSchemeValue(uri) == BroadcastDomainType.Pvlan;
 +            // should we consider moving this NetUtils method to
 +            // BroadcastDomainType?
 +            final long vlan = Long.parseLong(NetUtils.getPrimaryPvlanFromUri(uri));
 +            return enableVlanNetwork(conn, vlan, network);
 +        }
 +
 +        throw new CloudRuntimeException("Unable to support this type of network broadcast domain: " + nic.getBroadcastUri());
 +    }
 +
 +    /**
 +     * getNetworkByName() retrieves what the server thinks is the actual network
 +     * used by the XenServer host. This method should always be used to talk to
 +     * retrieve a network by the name. The reason is because of the problems in
 +     * using the name label as the way to find the Network.
 +     *
 +     * To see how we are working around these problems, take a look at
 +     * enableVlanNetwork(). The following description assumes you have looked at
 +     * the description on that method.
 +     *
 +     * In order to understand this, we have to see what type of networks are
 +     * within a XenServer that's under CloudStack control.
 +     *
 +     * - Native Networks: these are networks that are untagged on the XenServer
 +     * and are used to crate VLAN networks on. These are created by the user and
 +     * is assumed to be one per cluster. - VLAN Networks: these are dynamically
 +     * created by CloudStack and can have problems with duplicated names. -
 +     * LinkLocal Networks: these are dynamically created by CloudStack and can
 +     * also have problems with duplicated names but these don't have actual
 +     * PIFs.
 +     *
 +     * In order to speed to retrieval of a network, we do the following: - We
 +     * retrieve by the name. If only one network is retrieved, we assume we
 +     * retrieved the right network. - If more than one network is retrieved, we
 +     * check to see which one has the pif for the local host and use that. - If
 +     * a pif is not found, then we look at the tags and find the one with the
 +     * lowest timestamp. (See enableVlanNetwork())
 +     *
 +     * @param conn
 +     *            Xapi connection
 +     * @param name
 +     *            name of the network
 +     * @return XsNic an object that contains network, network record, pif, and
 +     *         pif record.
 +     * @throws XenAPIException
 +     * @throws XmlRpcException
 +     *
 +     * @see CitrixResourceBase#enableVlanNetwork
 +     */
 +    public XsLocalNetwork getNetworkByName(final Connection conn, final String name) throws XenAPIException, XmlRpcException {
 +        final Set<Network> networks = Network.getByNameLabel(conn, name);
 +        if (networks.size() == 1) {
 +            return new XsLocalNetwork(this, networks.iterator().next(), null, null, null);
 +        }
 +
 +        if (networks.size() == 0) {
 +            return null;
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Found more than one network with the name " + name);
 +        }
 +        Network earliestNetwork = null;
 +        Network.Record earliestNetworkRecord = null;
 +        long earliestTimestamp = Long.MAX_VALUE;
 +        int earliestRandom = Integer.MAX_VALUE;
 +        for (final Network network : networks) {
 +            final XsLocalNetwork nic = new XsLocalNetwork(this, network);
 +
 +            if (nic.getPif(conn) != null) {
 +                return nic;
 +            }
 +
 +            final Network.Record record = network.getRecord(conn);
 +            if (record.tags != null) {
 +                for (final String tag : record.tags) {
 +                    final Pair<Long, Integer> stamp = parseTimestamp(tag);
 +                    if (stamp == null) {
 +                        continue;
 +                    }
 +
 +                    if (stamp.first() < earliestTimestamp || stamp.first() == earliestTimestamp && stamp.second() < earliestRandom) {
 +                        earliestTimestamp = stamp.first();
 +                        earliestRandom = stamp.second();
 +                        earliestNetwork = network;
 +                        earliestNetworkRecord = record;
 +                    }
 +                }
 +            }
 +        }
 +
 +        return earliestNetwork != null ? new XsLocalNetwork(this, earliestNetwork, earliestNetworkRecord, null, null) : null;
 +    }
 +
 +    public long[] getNetworkStats(final Connection conn, final String privateIP) {
 +        final String result = networkUsage(conn, privateIP, "get", null);
 +        final long[] stats = new long[2];
 +        if (result != null) {
 +            final String[] splitResult = result.split(":");
 +            int i = 0;
 +            while (i < splitResult.length - 1) {
 +                stats[0] += Long.parseLong(splitResult[i++]);
 +                stats[1] += Long.parseLong(splitResult[i++]);
 +            }
 +        }
 +        return stats;
 +    }
 +
 +    public SR getNfsSR(final Connection conn, final String poolid, final String uuid, final String server, String serverpath, final String pooldesc) {
 +        final Map<String, String> deviceConfig = new HashMap<String, String>();
 +        try {
 +            serverpath = serverpath.replace("//", "/");
 +            final Set<SR> srs = SR.getAll(conn);
 +            if (srs != null && !srs.isEmpty()) {
 +                for (final SR sr : srs) {
 +                    if (!SRType.NFS.equals(sr.getType(conn))) {
 +                        continue;
 +                    }
 +
 +                    final Set<PBD> pbds = sr.getPBDs(conn);
 +                    if (pbds.isEmpty()) {
 +                        continue;
 +                    }
 +
 +                    final PBD pbd = pbds.iterator().next();
 +
 +                    final Map<String, String> dc = pbd.getDeviceConfig(conn);
 +
 +                    if (dc == null) {
 +                        continue;
 +                    }
 +
 +                    if (dc.get("server") == null) {
 +                        continue;
 +                    }
 +
 +                    if (dc.get("serverpath") == null) {
 +                        continue;
 +                    }
 +
 +                    if (server.equals(dc.get("server")) && serverpath.equals(dc.get("serverpath"))) {
 +                        throw new CloudRuntimeException(
 +                                "There is a SR using the same configuration server:" + dc.get("server") + ", serverpath:" + dc.get("serverpath") + " for pool " + uuid + " on host:" + _host.getUuid());
 +                    }
 +
 +                }
 +            }
 +            deviceConfig.put("server", server);
 +            deviceConfig.put("serverpath", serverpath);
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            final Map<String, String> smConfig = new HashMap<String, String>();
 +            smConfig.put("nosubdir", "true");
 +            final SR sr = SR.create(conn, host, deviceConfig, new Long(0), uuid, poolid, SRType.NFS.toString(), "user", true, smConfig);
 +            sr.scan(conn);
 +            return sr;
 +        } catch (final XenAPIException e) {
 +            throw new CloudRuntimeException("Unable to create NFS SR " + pooldesc, e);
 +        } catch (final XmlRpcException e) {
 +            throw new CloudRuntimeException("Unable to create NFS SR " + pooldesc, e);
 +        }
 +    }
 +
 +    private String getOvsTunnelNetworkName(final String broadcastUri) {
 +        if (broadcastUri.contains(".")) {
 +            final String[] parts = broadcastUri.split("\\.");
 +            return "OVS-DR-VPC-Bridge" + parts[0];
 +        } else {
 +            try {
 +                return "OVSTunnel" + broadcastUri;
 +            } catch (final Exception e) {
 +                return null;
 +            }
 +        }
 +    }
 +
 +    protected List<File> getPatchFiles() {
 +        String patch = getPatchFilePath();
 +        String patchfilePath = Script.findScript("", patch);
 +        if (patchfilePath == null) {
 +            throw new CloudRuntimeException("Unable to find patch file " + patch);
 +        }
 +        List<File> files = new ArrayList<File>();
 +        files.add(new File(patchfilePath));
 +        return files;
 +    }
 +
 +    protected abstract String getPatchFilePath();
 +
 +    public String getPerfMon(final Connection conn, final Map<String, String> params, final int wait) {
 +        String result = null;
 +        try {
 +            result = callHostPluginAsync(conn, "vmopspremium", "asmonitor", 60, params);
 +            if (result != null) {
 +                return result;
 +            }
 +        } catch (final Exception e) {
 +            s_logger.error("Can not get performance monitor for AS due to ", e);
 +        }
 +        return null;
 +    }
 +
 +    protected Object[] getRRDData(final Connection conn, final int flag) {
 +
 +        /*
 +         * Note: 1 => called from host, hence host stats 2 => called from vm,
 +         * hence vm stats
 +         */
 +        Document doc = null;
 +
 +        try {
 +            doc = getStatsRawXML(conn, flag == 1 ? true : false);
 +        } catch (final Exception e1) {
 +            s_logger.warn("Error whilst collecting raw stats from plugin: ", e1);
 +            return null;
 +        }
 +
 +        if (doc == null) { // stats are null when the host plugin call fails
 +            // (host down state)
 +            return null;
 +        }
 +
 +        final NodeList firstLevelChildren = doc.getChildNodes();
 +        final NodeList secondLevelChildren = firstLevelChildren.item(0).getChildNodes();
 +        final Node metaNode = secondLevelChildren.item(0);
 +        final Node dataNode = secondLevelChildren.item(1);
 +
 +        Integer numRows = 0;
 +        Integer numColumns = 0;
 +        Node legend = null;
 +        final NodeList metaNodeChildren = metaNode.getChildNodes();
 +        for (int i = 0; i < metaNodeChildren.getLength(); i++) {
 +            final Node n = metaNodeChildren.item(i);
 +            if (n.getNodeName().equals("rows")) {
 +                numRows = Integer.valueOf(getXMLNodeValue(n));
 +            } else if (n.getNodeName().equals("columns")) {
 +                numColumns = Integer.valueOf(getXMLNodeValue(n));
 +            } else if (n.getNodeName().equals("legend")) {
 +                legend = n;
 +            }
 +        }
 +
 +        return new Object[] {numRows, numColumns, legend, dataNode};
 +    }
 +
 +    @Override
 +    public int getRunLevel() {
 +        return 0;
 +    }
 +
 +    protected SR getSRByNameLabelandHost(final Connection conn, final String name) throws BadServerResponse, XenAPIException, XmlRpcException {
 +        final Set<SR> srs = SR.getByNameLabel(conn, name);
 +        SR ressr = null;
 +        for (final SR sr : srs) {
 +            Set<PBD> pbds;
 +            pbds = sr.getPBDs(conn);
 +            for (final PBD pbd : pbds) {
 +                final PBD.Record pbdr = pbd.getRecord(conn);
 +                if (pbdr.host != null && pbdr.host.getUuid(conn).equals(_host.getUuid())) {
 +                    if (!pbdr.currentlyAttached) {
 +                        pbd.plug(conn);
 +                    }
 +                    ressr = sr;
 +                    break;
 +                }
 +            }
 +        }
 +        return ressr;
 +    }
 +
 +    private long getStaticMax(final String os, final boolean b, final long dynamicMinRam, final long dynamicMaxRam, final long recommendedValue) {
 +        if (recommendedValue == 0) {
 +            s_logger.warn("No recommended value found for dynamic max, setting static max and dynamic max equal");
 +            return dynamicMaxRam;
 +        }
 +        final long staticMax = Math.min(recommendedValue, 4l * dynamicMinRam); // XS
 +        // constraint
 +        // for
 +        // stability
 +        if (dynamicMaxRam > staticMax) { // XS contraint that dynamic max <=
 +            // static max
 +            s_logger.warn("dynamixMax " + dynamicMaxRam + " cant be greater than static max " + staticMax + ", can lead to stability issues. Setting static max as much as dynamic max ");
 +            return dynamicMaxRam;
 +        }
 +        return staticMax;
 +    }
 +
 +    private long getStaticMin(final String os, final boolean b, final long dynamicMinRam, final long dynamicMaxRam, final long recommendedValue) {
 +        if (recommendedValue == 0) {
 +            s_logger.warn("No recommended value found for dynamic min");
 +            return dynamicMinRam;
 +        }
 +
 +        if (dynamicMinRam < recommendedValue) { // XS contraint that dynamic min
 +            // > static min
 +            s_logger.warn("Vm is set to dynamixMin " + dynamicMinRam + " less than the recommended static min " + recommendedValue + ", could lead to stability issues");
 +        }
 +        return dynamicMinRam;
 +    }
 +
 +    protected Document getStatsRawXML(final Connection conn, final boolean host) {
 +        final Date currentDate = new Date();
 +        String urlStr = "http://" + _host.getIp() + "/rrd_updates?";
 +        urlStr += "session_id=" + conn.getSessionReference();
 +        urlStr += "&host=" + (host ? "true" : "false");
 +        urlStr += "&cf=" + _consolidationFunction;
 +        urlStr += "&interval=" + _pollingIntervalInSeconds;
 +        urlStr += "&start=" + (currentDate.getTime() / 1000 - 1000 - 100);
 +
 +        URL url;
 +        BufferedReader in = null;
 +        try {
 +            url = new URL(urlStr);
 +            url.openConnection();
 +            final URLConnection uc = url.openConnection();
 +            in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
 +            final InputSource statsSource = new InputSource(in);
 +            return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(statsSource);
 +        } catch (final MalformedURLException e) {
 +            s_logger.warn("Malformed URL?  come on...." + urlStr);
 +            return null;
 +        } catch (final IOException e) {
 +            s_logger.warn("Problems getting stats using " + urlStr, e);
 +            return null;
 +        } catch (final SAXException e) {
 +            s_logger.warn("Problems getting stats using " + urlStr, e);
 +            return null;
 +        } catch (final ParserConfigurationException e) {
 +            s_logger.warn("Problems getting stats using " + urlStr, e);
 +            return null;
 +        } finally {
 +            if (in != null) {
 +                try {
 +                    in.close();
 +                } catch (final IOException e) {
 +                    s_logger.warn("Unable to close the buffer ", e);
 +                }
 +            }
 +        }
 +    }
 +
 +    public SR getStorageRepository(final Connection conn, final String srNameLabel) {
 +        Set<SR> srs;
 +        try {
 +            srs = SR.getByNameLabel(conn, srNameLabel);
 +        } catch (final XenAPIException e) {
 +            throw new CloudRuntimeException("Unable to get SR " + srNameLabel + " due to " + e.toString(), e);
 +        } catch (final Exception e) {
 +            throw new CloudRuntimeException("Unable to get SR " + srNameLabel + " due to " + e.getMessage(), e);
 +        }
 +
 +        if (srs.size() > 1) {
 +            throw new CloudRuntimeException("More than one storage repository was found for pool with uuid: " + srNameLabel);
 +        } else if (srs.size() == 1) {
 +            final SR sr = srs.iterator().next();
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("SR retrieved for " + srNameLabel);
 +            }
 +
 +            if (checkSR(conn, sr)) {
 +                return sr;
 +            }
 +            throw new CloudRuntimeException("SR check failed for storage pool: " + srNameLabel + "on host:" + _host.getUuid());
 +        } else {
 +            throw new CloudRuntimeException("Can not see storage pool: " + srNameLabel + " from on host:" + _host.getUuid());
 +        }
 +    }
 +
 +    protected Storage.StorageResourceType getStorageResourceType() {
 +        return Storage.StorageResourceType.STORAGE_POOL;
 +    }
 +
 +    @Override
 +    public Type getType() {
 +        return com.cloud.host.Host.Type.Routing;
 +    }
 +
 +    protected VDI getVDIbyLocationandSR(final Connection conn, final String loc, final SR sr) {
 +        try {
 +            final Set<VDI> vdis = sr.getVDIs(conn);
 +            for (final VDI vdi : vdis) {
 +                if (vdi.getLocation(conn).startsWith(loc)) {
 +                    return vdi;
 +                }
 +            }
 +
 +            final String msg = "can not getVDIbyLocationandSR " + loc;
 +            s_logger.warn(msg);
 +            return null;
 +        } catch (final XenAPIException e) {
 +            final String msg = "getVDIbyLocationandSR exception " + loc + " due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        } catch (final Exception e) {
 +            final String msg = "getVDIbyLocationandSR exception " + loc + " due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        }
 +
 +    }
 +
 +    public VDI getVDIbyUuid(final Connection conn, final String uuid) {
 +        return getVDIbyUuid(conn, uuid, true);
 +    }
 +
 +    public VDI getVDIbyUuid(final Connection conn, final String uuid, final boolean throwExceptionIfNotFound) {
 +        try {
 +            return VDI.getByUuid(conn, uuid);
 +        } catch (final Exception e) {
 +            if (throwExceptionIfNotFound) {
 +                final String msg = "Catch Exception " + e.getClass().getName() + " :VDI getByUuid for uuid: " + uuid + " failed due to " + e.toString();
 +
 +                s_logger.debug(msg);
 +
 +                throw new CloudRuntimeException(msg, e);
 +            }
 +
 +            return null;
 +        }
 +    }
 +
 +    public String getVhdParent(final Connection conn, final String primaryStorageSRUuid, final String snapshotUuid, final Boolean isISCSI) {
 +        final String parentUuid = callHostPlugin(conn, "vmopsSnapshot", "getVhdParent", "primaryStorageSRUuid", primaryStorageSRUuid, "snapshotUuid", snapshotUuid, "isISCSI", isISCSI.toString());
 +
 +        if (parentUuid == null || parentUuid.isEmpty() || parentUuid.equalsIgnoreCase("None")) {
 +            s_logger.debug("Unable to get parent of VHD " + snapshotUuid + " in SR " + primaryStorageSRUuid);
 +            // errString is already logged.
 +            return null;
 +        }
 +        return parentUuid;
 +    }
 +
 +    public VIF getVifByMac(final Connection conn, final VM router, String mac) throws XmlRpcException, XenAPIException {
 +        final Set<VIF> routerVIFs = router.getVIFs(conn);
 +        mac = mac.trim();
 +        for (final VIF vif : routerVIFs) {
 +            final String lmac = vif.getMAC(conn);
 +            if (lmac.trim().equals(mac)) {
 +                return vif;
 +            }
 +        }
 +        return null;
 +    }
 +
 +    public VirtualRoutingResource getVirtualRoutingResource() {
 +        return _vrResource;
 +    }
 +
 +    public VM getVM(final Connection conn, final String vmName) {
 +        // Look up VMs with the specified name
 +        Set<VM> vms;
 +        try {
 +            vms = VM.getByNameLabel(conn, vmName);
 +        } catch (final XenAPIException e) {
 +            throw new CloudRuntimeException("Unable to get " + vmName + ": " + e.toString(), e);
 +        } catch (final Exception e) {
 +            throw new CloudRuntimeException("Unable to get " + vmName + ": " + e.getMessage(), e);
 +        }
 +
 +        // If there are no VMs, throw an exception
 +        if (vms.size() == 0) {
 +            throw new CloudRuntimeException("VM with name: " + vmName + " does not exist.");
 +        }
 +
 +        // If there is more than one VM, print a warning
 +        if (vms.size() > 1) {
 +            s_logger.warn("Found " + vms.size() + " VMs with name: " + vmName);
 +        }
 +
 +        // Return the first VM in the set
 +        return vms.iterator().next();
 +    }
 +
 +    public String getVMInstanceName() {
 +        return _instance;
 +    }
 +
 +    public long getVMSnapshotChainSize(final Connection conn, final VolumeObjectTO volumeTo, final String vmName, final String vmSnapshotName)
 +            throws BadServerResponse, XenAPIException, XmlRpcException {
 +        if (volumeTo.getVolumeType() == Volume.Type.DATADISK) {
 +            final VDI dataDisk = VDI.getByUuid(conn, volumeTo.getPath());
 +            if (dataDisk != null) {
 +                final String dataDiskName = dataDisk.getNameLabel(conn);
 +                if (dataDiskName != null && !dataDiskName.isEmpty()) {
 +                    volumeTo.setName(dataDiskName);
 +                }
 +            }
 +        }
 +        final Set<VDI> allvolumeVDIs = VDI.getByNameLabel(conn, volumeTo.getName());
 +        long size = 0;
 +        for (final VDI vdi : allvolumeVDIs) {
 +            try {
 +                if (vdi.getIsASnapshot(conn) && vdi.getSmConfig(conn).get("vhd-parent") != null) {
 +                    final String parentUuid = vdi.getSmConfig(conn).get("vhd-parent");
 +                    final VDI parentVDI = VDI.getByUuid(conn, parentUuid);
 +                    // add size of snapshot vdi node, usually this only contains
 +                    // meta data
 +                    size = size + vdi.getPhysicalUtilisation(conn);
 +                    // add size of snapshot vdi parent, this contains data
 +                    if (!isRefNull(parentVDI)) {
 +                        size = size + parentVDI.getPhysicalUtilisation(conn).longValue();
 +                    }
 +                }
 +            } catch (final Exception e) {
 +                s_logger.debug("Exception occurs when calculate snapshot capacity for volumes: due to " + e.toString());
 +                continue;
 +            }
 +        }
 +        if (volumeTo.getVolumeType() == Volume.Type.ROOT) {
 +            VM vm = getVM(conn, vmName);
 +            if (vm != null) {
 +                Set<VM> vmSnapshots = vm.getSnapshots(conn);
 +                if (vmSnapshots != null) {
 +                    for (VM vmsnap : vmSnapshots) {
 +                        try {
 +                            final String vmSnapName = vmsnap.getNameLabel(conn);
 +                            s_logger.debug("snapname " + vmSnapName);
 +                            if (vmSnapName != null && vmSnapName.contains(vmSnapshotName) && vmsnap.getIsASnapshot(conn)) {
 +                                s_logger.debug("snapname " + vmSnapName + "isASnapshot");
 +                                VDI memoryVDI = vmsnap.getSuspendVDI(conn);
 +                                if (!isRefNull(memoryVDI)) {
 +                                    size = size + memoryVDI.getPhysicalUtilisation(conn);
 +                                    s_logger.debug("memoryVDI size :" + size);
 +                                    String parentUuid = memoryVDI.getSmConfig(conn).get("vhd-parent");
 +                                    VDI pMemoryVDI = VDI.getByUuid(conn, parentUuid);
 +                                    if (!isRefNull(pMemoryVDI)) {
 +                                        size = size + pMemoryVDI.getPhysicalUtilisation(conn);
 +                                    }
 +                                    s_logger.debug("memoryVDI size+parent :" + size);
 +                                }
 +                            }
 +                        } catch (Exception e) {
 +                            s_logger.debug("Exception occurs when calculate snapshot capacity for memory: due to " + e.toString());
 +                            continue;
 +                        }
 +
 +                    }
 +                }
 +            }
 +        }
 +        return size;
 +    }
 +
 +    public PowerState getVmState(final Connection conn, final String vmName) {
 +        int retry = 3;
 +        while (retry-- > 0) {
 +            try {
 +                final Set<VM> vms = VM.getByNameLabel(conn, vmName);
 +                for (final VM vm : vms) {
 +                    return convertToPowerState(vm.getPowerState(conn));
 +                }
 +            } catch (final BadServerResponse e) {
 +                // There is a race condition within xenserver such that if a vm
 +                // is
 +                // deleted and we
 +                // happen to ask for it, it throws this stupid response. So
 +                // if this happens,
 +                // we take a nap and try again which then avoids the race
 +                // condition because
 +                // the vm's information is now cleaned up by xenserver. The
 +                // error
 +                // is as follows
 +                // com.xensource.xenapi.Types$BadServerResponse
 +                // [HANDLE_INVALID, VM,
 +                // 3dde93f9-c1df-55a7-2cde-55e1dce431ab]
 +                s_logger.info("Unable to get a vm PowerState due to " + e.toString() + ". We are retrying.  Count: " + retry);
 +                try {
 +                    Thread.sleep(3000);
 +                } catch (final InterruptedException ex) {
 +
 +                }
 +            } catch (final XenAPIException e) {
 +                final String msg = "Unable to get a vm PowerState due to " + e.toString();
 +                s_logger.warn(msg, e);
 +                break;
 +            } catch (final XmlRpcException e) {
 +                final String msg = "Unable to get a vm PowerState due to " + e.getMessage();
 +                s_logger.warn(msg, e);
 +                break;
 +            }
 +        }
 +
 +        return PowerState.PowerOff;
 +    }
 +
 +    public HashMap<String, VmStatsEntry> getVmStats(final Connection conn, final GetVmStatsCommand cmd, final List<String> vmUUIDs, final String hostGuid) {
 +        final HashMap<String, VmStatsEntry> vmResponseMap = new HashMap<String, VmStatsEntry>();
 +
 +        for (final String vmUUID : vmUUIDs) {
 +            vmResponseMap.put(vmUUID, new VmStatsEntry(0, 0, 0, 0, 0, 0, 0, "vm"));
 +        }
 +
 +        final Object[] rrdData = getRRDData(conn, 2); // call rrddata with 2 for
 +        // vm
 +
 +        if (rrdData == null) {
 +            return null;
 +        }
 +
 +        final Integer numRows = (Integer)rrdData[0];
 +        final Integer numColumns = (Integer)rrdData[1];
 +        final Node legend = (Node)rrdData[2];
 +        final Node dataNode = (Node)rrdData[3];
 +
 +        final NodeList legendChildren = legend.getChildNodes();
 +        for (int col = 0; col < numColumns; col++) {
 +
 +            if (legendChildren == null || legendChildren.item(col) == null) {
 +                continue;
 +            }
 +
 +            final String columnMetadata = getXMLNodeValue(legendChildren.item(col));
 +
 +            if (columnMetadata == null) {
 +                continue;
 +            }
 +
 +            final String[] columnMetadataList = columnMetadata.split(":");
 +
 +            if (columnMetadataList.length != 4) {
 +                continue;
 +            }
 +
 +            final String type = columnMetadataList[1];
 +            final String uuid = columnMetadataList[2];
 +            final String param = columnMetadataList[3];
 +
 +            if (type.equals("vm") && vmResponseMap.keySet().contains(uuid)) {
 +                final VmStatsEntry vmStatsAnswer = vmResponseMap.get(uuid);
 +
 +                vmStatsAnswer.setEntityType("vm");
 +
 +                if (param.contains("cpu")) {
 +                    vmStatsAnswer.setNumCPUs(vmStatsAnswer.getNumCPUs() + 1);
 +                    vmStatsAnswer.setCPUUtilization(vmStatsAnswer.getCPUUtilization() + getDataAverage(dataNode, col, numRows));
 +                } else if (param.matches("vif_\\d*_rx")) {
 +                    vmStatsAnswer.setNetworkReadKBs(vmStatsAnswer.getNetworkReadKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                } else if (param.matches("vif_\\d*_tx")) {
 +                    vmStatsAnswer.setNetworkWriteKBs(vmStatsAnswer.getNetworkWriteKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                } else if (param.matches("vbd_.*_read")) {
 +                    vmStatsAnswer.setDiskReadKBs(vmStatsAnswer.getDiskReadKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                } else if (param.matches("vbd_.*_write")) {
 +                    vmStatsAnswer.setDiskWriteKBs(vmStatsAnswer.getDiskWriteKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                } else if (param.contains("memory_internal_free")) {
 +                    vmStatsAnswer.setIntFreeMemoryKBs(vmStatsAnswer.getIntFreeMemoryKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                } else if (param.contains("memory_target")) {
 +                    vmStatsAnswer.setTargetMemoryKBs(vmStatsAnswer.getTargetMemoryKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                } else if (param.contains("memory")) {
 +                    vmStatsAnswer.setMemoryKBs(vmStatsAnswer.getMemoryKBs() + getDataAverage(dataNode, col, numRows) / BASE_TO_CONVERT_BYTES_INTO_KILOBYTES);
 +                }
 +
 +            }
 +        }
 +
 +        for (final Map.Entry<String, VmStatsEntry> entry : vmResponseMap.entrySet()) {
 +            final VmStatsEntry vmStatsAnswer = entry.getValue();
 +
 +            if (vmStatsAnswer.getNumCPUs() != 0) {
 +                vmStatsAnswer.setCPUUtilization(vmStatsAnswer.getCPUUtilization() / vmStatsAnswer.getNumCPUs());
 +            }
 +
 +            vmStatsAnswer.setCPUUtilization(vmStatsAnswer.getCPUUtilization() * 100);
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Vm cpu utilization " + vmStatsAnswer.getCPUUtilization());
 +            }
 +        }
 +
 +        return vmResponseMap;
 +    }
 +
 +    public String getVncUrl(final Connection conn, final VM vm) {
 +        VM.Record record;
 +        Console c;
 +        try {
 +            record = vm.getRecord(conn);
 +            final Set<Console> consoles = record.consoles;
 +
 +            if (consoles.isEmpty()) {
 +                s_logger.warn("There are no Consoles available to the vm : " + record.nameDescription);
 +                return null;
 +            }
 +            final Iterator<Console> i = consoles.iterator();
 +            while (i.hasNext()) {
 +                c = i.next();
 +                if (c.getProtocol(conn) == Types.ConsoleProtocol.RFB) {
 +                    return c.getLocation(conn);
 +                }
 +            }
 +        } catch (final XenAPIException e) {
 +            final String msg = "Unable to get console url due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            return null;
 +        } catch (final XmlRpcException e) {
 +            final String msg = "Unable to get console url due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +            return null;
 +        }
 +        return null;
 +    }
 +
 +    protected String getXMLNodeValue(final Node n) {
 +        return n.getChildNodes().item(0).getNodeValue();
 +    }
 +
 +    public void handleSrAndVdiDetach(final String iqn, final Connection conn) throws Exception {
 +        final SR sr = getStorageRepository(conn, iqn);
 +
 +        removeSR(conn, sr);
 +    }
 +
 +    protected void destroyUnattachedVBD(Connection conn, VM vm) {
 +        try {
 +            for (VBD vbd : vm.getVBDs(conn)) {
 +                if (Types.VbdType.DISK.equals(vbd.getType(conn)) && !vbd.getCurrentlyAttached(conn)) {
 +                    vbd.destroy(conn);
 +                }
 +            }
 +        } catch (final Exception e) {
 +            s_logger.debug("Failed to destroy unattached VBD due to ", e);
 +        }
 +    }
 +
 +    public String handleVmStartFailure(final Connection conn, final String vmName, final VM vm, final String message, final Throwable th) {
 +        final String msg = "Unable to start " + vmName + " due to " + message;
 +        s_logger.warn(msg, th);
 +
 +        if (vm == null) {
 +            return msg;
 +        }
 +
 +        try {
 +            final VM.Record vmr = vm.getRecord(conn);
 +            final List<Network> networks = new ArrayList<Network>();
 +            for (final VIF vif : vmr.VIFs) {
 +                try {
 +                    final VIF.Record rec = vif.getRecord(conn);
 +                    if (rec != null) {
 +                        networks.add(rec.network);
 +                    } else {
 +                        s_logger.warn("Unable to cleanup VIF: " + vif.toWireString() + " As vif record is null");
 +                    }
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to cleanup VIF", e);
 +                }
 +            }
 +            if (vmr.powerState == VmPowerState.RUNNING) {
 +                try {
 +                    vm.hardShutdown(conn);
 +                } catch (final Exception e) {
 +                    s_logger.warn("VM hardshutdown failed due to ", e);
 +                }
 +            }
 +            if (vm.getPowerState(conn) == VmPowerState.HALTED) {
 +                try {
 +                    vm.destroy(conn);
 +                } catch (final Exception e) {
 +                    s_logger.warn("VM destroy failed due to ", e);
 +                }
 +            }
 +            for (final VBD vbd : vmr.VBDs) {
 +                try {
 +                    vbd.unplug(conn);
 +                    vbd.destroy(conn);
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to clean up VBD due to ", e);
 +                }
 +            }
 +            for (final VIF vif : vmr.VIFs) {
 +                try {
 +                    vif.unplug(conn);
 +                    vif.destroy(conn);
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to cleanup VIF", e);
 +                }
 +            }
 +            for (final Network network : networks) {
 +                if (network.getNameLabel(conn).startsWith("VLAN")) {
 +                    disableVlanNetwork(conn, network);
 +                }
 +            }
 +        } catch (final Exception e) {
 +            s_logger.warn("VM getRecord failed due to ", e);
 +        }
 +
 +        return msg;
 +    }
 +
 +    @Override
 +    public StartupCommand[] initialize() throws IllegalArgumentException {
 +        final Connection conn = getConnection();
 +        if (!getHostInfo(conn)) {
 +            s_logger.warn("Unable to get host information for " + _host.getIp());
 +            return null;
 +        }
 +        final StartupRoutingCommand cmd = new StartupRoutingCommand();
 +        fillHostInfo(conn, cmd);
 +        cmd.setHypervisorType(HypervisorType.XenServer);
 +        cmd.setCluster(_cluster);
 +        cmd.setPoolSync(false);
 +
 +        try {
 +            final Pool pool = Pool.getByUuid(conn, _host.getPool());
 +            final Pool.Record poolr = pool.getRecord(conn);
 +            poolr.master.getRecord(conn);
 +        } catch (final Throwable e) {
 +            s_logger.warn("Check for master failed, failing the FULL Cluster sync command");
 +        }
 +        final StartupStorageCommand sscmd = initializeLocalSR(conn);
 +        if (sscmd != null) {
 +            return new StartupCommand[] {cmd, sscmd};
 +        }
 +        return new StartupCommand[] {cmd};
 +    }
 +
 +    protected StartupStorageCommand initializeLocalSR(final Connection conn) {
 +        final SR lvmsr = getLocalLVMSR(conn);
 +        if (lvmsr != null) {
 +            try {
 +                _host.setLocalSRuuid(lvmsr.getUuid(conn));
 +
 +                final String lvmuuid = lvmsr.getUuid(conn);
 +                final long cap = lvmsr.getPhysicalSize(conn);
 +                if (cap > 0) {
 +                    final long avail = cap - lvmsr.getPhysicalUtilisation(conn);
 +                    lvmsr.setNameLabel(conn, lvmuuid);
 +                    final String name = "Cloud Stack Local LVM Storage Pool for " + _host.getUuid();
 +                    lvmsr.setNameDescription(conn, name);
 +                    final Host host = Host.getByUuid(conn, _host.getUuid());
 +                    final String address = host.getAddress(conn);
 +                    final StoragePoolInfo pInfo = new StoragePoolInfo(lvmuuid, address, SRType.LVM.toString(), SRType.LVM.toString(), StoragePoolType.LVM, cap, avail);
 +                    final StartupStorageCommand cmd = new StartupStorageCommand();
 +                    cmd.setPoolInfo(pInfo);
 +                    cmd.setGuid(_host.getUuid());
 +                    cmd.setDataCenter(Long.toString(_dcId));
 +                    cmd.setResourceType(Storage.StorageResourceType.STORAGE_POOL);
 +                    return cmd;
 +                }
 +            } catch (final XenAPIException e) {
 +                final String msg = "build local LVM info err in host:" + _host.getUuid() + e.toString();
 +                s_logger.warn(msg);
 +            } catch (final XmlRpcException e) {
 +                final String msg = "build local LVM info err in host:" + _host.getUuid() + e.getMessage();
 +                s_logger.warn(msg);
 +            }
 +        }
 +
 +        final SR extsr = getLocalEXTSR(conn);
 +        if (extsr != null) {
 +            try {
 +                final String extuuid = extsr.getUuid(conn);
 +                _host.setLocalSRuuid(extuuid);
 +                final long cap = extsr.getPhysicalSize(conn);
 +                if (cap > 0) {
 +                    final long avail = cap - extsr.getPhysicalUtilisation(conn);
 +                    extsr.setNameLabel(conn, extuuid);
 +                    final String name = "Cloud Stack Local EXT Storage Pool for " + _host.getUuid();
 +                    extsr.setNameDescription(conn, name);
 +                    final Host host = Host.getByUuid(conn, _host.getUuid());
 +                    final String address = host.getAddress(conn);
 +                    final StoragePoolInfo pInfo = new StoragePoolInfo(extuuid, address, SRType.EXT.toString(), SRType.EXT.toString(), StoragePoolType.EXT, cap, avail);
 +                    final StartupStorageCommand cmd = new StartupStorageCommand();
 +                    cmd.setPoolInfo(pInfo);
 +                    cmd.setGuid(_host.getUuid());
 +                    cmd.setDataCenter(Long.toString(_dcId));
 +                    cmd.setResourceType(Storage.StorageResourceType.STORAGE_POOL);
 +                    return cmd;
 +                }
 +            } catch (final XenAPIException e) {
 +                final String msg = "build local EXT info err in host:" + _host.getUuid() + e.toString();
 +                s_logger.warn(msg);
 +            } catch (final XmlRpcException e) {
 +                final String msg = "build local EXT info err in host:" + _host.getUuid() + e.getMessage();
 +                s_logger.warn(msg);
 +            }
 +        }
 +        return null;
 +    }
 +
 +    public boolean isDeviceUsed(final Connection conn, final VM vm, final Long deviceId) {
 +        // Figure out the disk number to attach the VM to
 +
 +        String msg = null;
 +        try {
 +            final Set<String> allowedVBDDevices = vm.getAllowedVBDDevices(conn);
 +            if (allowedVBDDevices.contains(deviceId.toString())) {
 +                return false;
 +            }
 +            return true;
 +        } catch (final XmlRpcException e) {
 +            msg = "Catch XmlRpcException due to: " + e.getMessage();
 +            s_logger.warn(msg, e);
 +        } catch (final XenAPIException e) {
 +            msg = "Catch XenAPIException due to: " + e.toString();
 +            s_logger.warn(msg, e);
 +        }
 +        throw new CloudRuntimeException("When check deviceId " + msg);
 +    }
 +
 +    /**
 +     * When Dynamic Memory Control (DMC) is enabled - xenserver allows scaling
 +     * the guest memory while the guest is running
 +     *
 +     * By default this is disallowed, override the specific xenserver resource
 +     * if this is enabled
 +     */
 +    public boolean isDmcEnabled(final Connection conn, final Host host) throws XenAPIException, XmlRpcException {
 +        return false;
 +    }
 +
 +    public boolean IsISCSI(final String type) {
 +        return SRType.LVMOHBA.equals(type) || SRType.LVMOISCSI.equals(type) || SRType.LVM.equals(type);
 +    }
 +
 +    public boolean isNetworkSetupByName(final String nameTag) throws XenAPIException, XmlRpcException {
 +        if (nameTag != null) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Looking for network setup by name " + nameTag);
 +            }
 +            final Connection conn = getConnection();
 +            final XsLocalNetwork network = getNetworkByName(conn, nameTag);
 +            if (network == null) {
 +                return false;
 +            }
 +        }
 +        return true;
 +    }
 +
 +    public boolean isOvs() {
 +        return _isOvs;
 +    }
 +
 +    public boolean isRefNull(final XenAPIObject object) {
 +        return object == null || object.toWireString().equals("OpaqueRef:NULL") || object.toWireString().equals("<not in database>");
 +    }
 +
 +    public boolean isSecurityGroupEnabled() {
 +        return _securityGroupEnabled;
 +    }
 +
 +    public boolean isXcp() {
 +        final Connection conn = getConnection();
 +        final String result = callHostPlugin(conn, "ovstunnel", "is_xcp");
 +        if (result.equals("XCP")) {
 +            return true;
 +        }
 +        return false;
 +    }
 +
 +    boolean killCopyProcess(final Connection conn, final String nameLabel) {
 +        final String results = callHostPluginAsync(conn, "vmops", "kill_copy_process", 60, "namelabel", nameLabel);
 +        String errMsg = null;
 +        if (results == null || results.equals("false")) {
 +            errMsg = "kill_copy_process failed";
 +            s_logger.warn(errMsg);
 +            return false;
 +        } else {
 +            return true;
 +        }
 +    }
 +
 +    public boolean launchHeartBeat(final Connection conn) {
 +        final String result = callHostPluginPremium(conn, "heartbeat", "host", _host.getUuid(), "timeout", Integer.toString(_heartbeatTimeout), "interval", Integer.toString(_heartbeatInterval));
 +        if (result == null || !result.contains("> DONE <")) {
 +            s_logger.warn("Unable to launch the heartbeat process on " + _host.getIp());
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    protected String logX(final XenAPIObject obj, final String msg) {
 +        return new StringBuilder("Host ").append(_host.getIp()).append(" ").append(obj.toWireString()).append(": ").append(msg).toString();
 +    }
 +
 +    public void migrateVM(final Connection conn, final Host destHost, final VM vm, final String vmName) throws Exception {
 +        Task task = null;
 +        try {
 +            final Map<String, String> other = new HashMap<String, String>();
 +            other.put("live", "true");
 +            task = vm.poolMigrateAsync(conn, destHost, other);
 +            try {
 +                // poll every 1 seconds
 +                final long timeout = _migratewait * 1000L;
 +                waitForTask(conn, task, 1000, timeout);
 +                checkForSuccess(conn, task);
 +            } catch (final Types.HandleInvalid e) {
 +                if (vm.getResidentOn(conn).equals(destHost)) {
 +                    task = null;
 +                    return;
 +                }
 +                throw new CloudRuntimeException("migrate VM catch HandleInvalid and VM is not running on dest host");
 +            }
 +        } catch (final XenAPIException e) {
 +            final String msg = "Unable to migrate VM(" + vmName + ") from host(" + _host.getUuid() + ")";
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg);
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e1) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e1.toString());
 +                }
 +            }
 +        }
 +    }
 +
 +    protected VDI mount(final Connection conn, final StoragePoolType poolType, final String volumeFolder, final String volumePath) {
 +        return getVDIbyUuid(conn, volumePath);
 +    }
 +
 +    protected VDI mount(final Connection conn, final String vmName, final DiskTO volume) throws XmlRpcException, XenAPIException {
 +        final DataTO data = volume.getData();
 +        final Volume.Type type = volume.getType();
 +        if (type == Volume.Type.ISO) {
 +            final TemplateObjectTO iso = (TemplateObjectTO)data;
 +            final DataStoreTO store = iso.getDataStore();
 +
 +            if (store == null) {
 +                // It's a fake iso
 +                return null;
 +            }
 +
 +            // corer case, xenserver pv driver iso
 +            final String templateName = iso.getName();
 +            if (templateName.startsWith("xs-tools")) {
 +                try {
 +                    final String actualTemplateName = getActualIsoTemplate(conn);
 +                    final Set<VDI> vdis = VDI.getByNameLabel(conn, actualTemplateName);
 +                    if (vdis.isEmpty()) {
 +                        throw new CloudRuntimeException("Could not find ISO with URL: " + actualTemplateName);
 +                    }
 +                    return vdis.iterator().next();
 +                } catch (final XenAPIException e) {
 +                    throw new CloudRuntimeException("Unable to get pv iso: " + templateName + " due to " + e.toString());
 +                } catch (final Exception e) {
 +                    throw new CloudRuntimeException("Unable to get pv iso: " + templateName + " due to " + e.toString());
 +                }
 +            }
 +
 +            if (!(store instanceof NfsTO)) {
 +                throw new CloudRuntimeException("only support mount iso on nfs");
 +            }
 +            final NfsTO nfsStore = (NfsTO)store;
 +            final String isoPath = nfsStore.getUrl() + File.separator + iso.getPath();
 +            final int index = isoPath.lastIndexOf("/");
 +
 +            final String mountpoint = isoPath.substring(0, index);
 +            URI uri;
 +            try {
 +                uri = new URI(mountpoint);
 +            } catch (final URISyntaxException e) {
 +                throw new CloudRuntimeException("Incorrect uri " + mountpoint, e);
 +            }
 +            final SR isoSr = createIsoSRbyURI(conn, uri, vmName, false);
 +
 +            final String isoname = isoPath.substring(index + 1);
 +
 +            final VDI isoVdi = getVDIbyLocationandSR(conn, isoname, isoSr);
 +
 +            if (isoVdi == null) {
 +                throw new CloudRuntimeException("Unable to find ISO " + isoPath);
 +            }
 +            return isoVdi;
 +        } else {
 +            final VolumeObjectTO vol = (VolumeObjectTO)data;
 +            return VDI.getByUuid(conn, vol.getPath());
 +        }
 +    }
 +
 +    public String networkUsage(final Connection conn, final String privateIpAddress, final String option, final String vif) {
 +        if (option.equals("get")) {
 +            return "0:0";
 +        }
 +        return null;
 +    }
 +
 +    private List<Pair<String, Long>> ovsFullSyncStates() {
 +        final Connection conn = getConnection();
 +        final String result = callHostPlugin(conn, "ovsgre", "ovs_get_vm_log", "host_uuid", _host.getUuid());
 +        final String[] logs = result != null ? result.split(";") : new String[0];
 +        final List<Pair<String, Long>> states = new ArrayList<Pair<String, Long>>();
 +        for (final String log : logs) {
 +            final String[] info = log.split(",");
 +            if (info.length != 5) {
 +                s_logger.warn("Wrong element number in ovs log(" + log + ")");
 +                continue;
 +            }
 +
 +            // ','.join([bridge, vmName, vmId, seqno, tag])
 +            try {
 +                states.add(new Pair<String, Long>(info[0], Long.parseLong(info[3])));
 +            } catch (final NumberFormatException nfe) {
 +                states.add(new Pair<String, Long>(info[0], -1L));
 +            }
 +        }
 +        return states;
 +    }
 +
 +    public HashMap<String, String> parseDefaultOvsRuleComamnd(final String str) {
 +        final HashMap<String, String> cmd = new HashMap<String, String>();
 +        final String[] sarr = str.split("/");
 +        for (int i = 0; i < sarr.length; i++) {
 +            String c = sarr[i];
 +            c = c.startsWith("/") ? c.substring(1) : c;
 +            c = c.endsWith("/") ? c.substring(0, c.length() - 1) : c;
 +            final String[] p = c.split(";");
 +            if (p.length != 2) {
 +                continue;
 +            }
 +            if (p[0].equalsIgnoreCase("vlans")) {
 +                p[1] = p[1].replace("@", "[");
 +                p[1] = p[1].replace("#", "]");
 +            }
 +            cmd.put(p[0], p[1]);
 +        }
 +        return cmd;
 +    }
 +
 +    protected Pair<Long, Integer> parseTimestamp(final String timeStampStr) {
 +        final String[] tokens = timeStampStr.split("-");
 +        if (tokens.length != 3) {
 +            s_logger.debug("timeStamp in network has wrong pattern: " + timeStampStr);
 +            return null;
 +        }
 +        if (!tokens[0].equals("CsCreateTime")) {
 +            s_logger.debug("timeStamp in network doesn't start with CsCreateTime: " + timeStampStr);
 +            return null;
 +        }
 +        return new Pair<Long, Integer>(Long.parseLong(tokens[1]), Integer.parseInt(tokens[2]));
 +    }
 +
 +    private void pbdPlug(final Connection conn, final PBD pbd, final String uuid) {
 +        try {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Plugging in PBD " + uuid + " for " + _host);
 +            }
 +            pbd.plug(conn);
 +        } catch (final Exception e) {
 +            final String msg = "PBD " + uuid + " is not attached! and PBD plug failed due to " + e.toString() + ". Please check this PBD in " + _host;
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg);
 +        }
 +    }
 +
 +    protected boolean pingdomr(final Connection conn, final String host, final String port) {
 +        String status;
 +        status = callHostPlugin(conn, "vmops", "pingdomr", "host", host, "port", port);
 +
 +        if (status == null || status.isEmpty()) {
 +            return false;
 +        }
 +
 +        return true;
 +
 +    }
 +
 +    public boolean pingXAPI() {
 +        final Connection conn = getConnection();
 +        try {
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            if (!host.getEnabled(conn)) {
 +                s_logger.debug("Host " + _host.getIp() + " is not enabled!");
 +                return false;
 +            }
 +        } catch (final Exception e) {
 +            s_logger.debug("cannot get host enabled status, host " + _host.getIp() + " due to " + e.toString(), e);
 +            return false;
 +        }
 +        try {
 +            callHostPlugin(conn, "echo", "main");
 +        } catch (final Exception e) {
 +            s_logger.debug("cannot ping host " + _host.getIp() + " due to " + e.toString(), e);
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    protected void plugDom0Vif(final Connection conn, final VIF dom0Vif) throws XmlRpcException, XenAPIException {
 +        if (dom0Vif != null) {
 +            dom0Vif.plug(conn);
 +        }
 +    }
 +
 +    protected boolean postCreatePrivateTemplate(final Connection conn, final String templatePath, final String tmpltFilename, final String templateName, String templateDescription, String checksum,
 +            final long size, final long virtualSize, final long templateId) {
 +
 +        if (templateDescription == null) {
 +            templateDescription = "";
 +        }
 +
 +        if (checksum == null) {
 +            checksum = "";
 +        }
 +
 +        final String result = callHostPlugin(conn, "vmopsSnapshot", "post_create_private_template", "templatePath", templatePath, "templateFilename", tmpltFilename, "templateName", templateName,
 +                "templateDescription", templateDescription, "checksum", checksum, "size", String.valueOf(size), "virtualSize", String.valueOf(virtualSize), "templateId", String.valueOf(templateId));
 +
 +        boolean success = false;
 +        if (result != null && !result.isEmpty()) {
 +            // Else, command threw an exception which has already been logged.
 +
 +            if (result.equalsIgnoreCase("1")) {
 +                s_logger.debug("Successfully created template.properties file on secondary storage for " + tmpltFilename);
 +                success = true;
 +            } else {
 +                s_logger.warn("Could not create template.properties file on secondary storage for " + tmpltFilename + " for templateId: " + templateId);
 +            }
 +        }
 +
 +        return success;
 +    }
 +
 +    @Override
 +    public ExecutionResult prepareCommand(final NetworkElementCommand cmd) {
 +        // Update IP used to access router
 +        cmd.setRouterAccessIp(cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP));
 +        assert cmd.getRouterAccessIp() != null;
 +
 +        if (cmd instanceof IpAssocVpcCommand) {
 +            return prepareNetworkElementCommand((IpAssocVpcCommand)cmd);
 +        } else if (cmd instanceof IpAssocCommand) {
 +            return prepareNetworkElementCommand((IpAssocCommand)cmd);
 +        } else if (cmd instanceof SetupGuestNetworkCommand) {
 +            return prepareNetworkElementCommand((SetupGuestNetworkCommand)cmd);
 +        } else if (cmd instanceof SetSourceNatCommand) {
 +            return prepareNetworkElementCommand((SetSourceNatCommand)cmd);
 +        } else if (cmd instanceof SetNetworkACLCommand) {
 +            return prepareNetworkElementCommand((SetNetworkACLCommand)cmd);
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    public void prepareISO(final Connection conn, final String vmName, List<String[]> vmDataList, String configDriveLabel) throws XmlRpcException, XenAPIException {
 +
 +        final Set<VM> vms = VM.getByNameLabel(conn, vmName);
 +        if (vms == null || vms.size() != 1) {
 +            throw new CloudRuntimeException("There are " + (vms == null ? "0" : vms.size()) + " VMs named " + vmName);
 +        }
 +        final VM vm = vms.iterator().next();
 +
 +        if (vmDataList != null) {
 +            // create SR
 +            SR sr = createLocalIsoSR(conn, _configDriveSRName + getHost().getIp());
 +
 +            // 1. create vm data files
 +            createVmdataFiles(vmName, vmDataList, configDriveLabel);
 +
 +            // 2. copy config drive iso to host
 +            copyConfigDriveIsoToHost(conn, sr, vmName);
 +        }
 +
 +        final Set<VBD> vbds = vm.getVBDs(conn);
 +        for (final VBD vbd : vbds) {
 +            final VBD.Record vbdr = vbd.getRecord(conn);
 +            if (vbdr.type == Types.VbdType.CD && vbdr.empty == false && vbdr.userdevice.equals(_attachIsoDeviceNum)) {
 +                final VDI vdi = vbdr.VDI;
 +                final SR sr = vdi.getSR(conn);
 +                final Set<PBD> pbds = sr.getPBDs(conn);
 +                if (pbds == null) {
 +                    throw new CloudRuntimeException("There is no pbd for sr " + sr);
 +                }
 +                for (final PBD pbd : pbds) {
 +                    final PBD.Record pbdr = pbd.getRecord(conn);
 +                    if (pbdr.host.getUuid(conn).equals(_host.getUuid())) {
 +                        return;
 +                    }
 +                }
 +                sr.setShared(conn, true);
 +                final Host host = Host.getByUuid(conn, _host.getUuid());
 +                final PBD.Record pbdr = pbds.iterator().next().getRecord(conn);
 +                pbdr.host = host;
 +                pbdr.uuid = "";
 +                final PBD pbd = PBD.create(conn, pbdr);
 +                pbdPlug(conn, pbd, pbd.getUuid(conn));
 +                break;
 +            }
 +        }
 +    }
 +
 +    // The idea here is to see if the DiskTO in question is from managed storage and does not yet have an SR.
 +    // If no SR, create it and create a VDI in it.
 +    public VDI prepareManagedDisk(final Connection conn, final DiskTO disk, final long vmId, final String vmName) throws Exception {
 +        final Map<String, String> details = disk.getDetails();
 +
 +        if (details == null) {
 +            return null;
 +        }
 +
 +        final boolean isManaged = new Boolean(details.get(DiskTO.MANAGED)).booleanValue();
 +
 +        if (!isManaged) {
 +            return null;
 +        }
 +
 +        final String iqn = details.get(DiskTO.IQN);
 +
 +        final Set<SR> srNameLabels = SR.getByNameLabel(conn, iqn);
 +
 +        if (srNameLabels.size() != 0) {
 +            return null;
 +        }
 +
 +        final String vdiNameLabel = Volume.Type.ROOT.equals(disk.getType()) ? ("ROOT-" + vmId) : (vmName + "-DATA");
 +
 +        return prepareManagedStorage(conn, details, null, vdiNameLabel);
 +    }
 +
 +    protected SR prepareManagedSr(final Connection conn, final Map<String, String> details) {
 +        final String iScsiName = details.get(DiskTO.IQN);
 +        final String storageHost = details.get(DiskTO.STORAGE_HOST);
 +        final String chapInitiatorUsername = details.get(DiskTO.CHAP_INITIATOR_USERNAME);
 +        final String chapInitiatorSecret = details.get(DiskTO.CHAP_INITIATOR_SECRET);
 +        final String mountpoint = details.get(DiskTO.MOUNT_POINT);
 +        final String protocoltype = details.get(DiskTO.PROTOCOL_TYPE);
 +
 +        if (StoragePoolType.NetworkFilesystem.toString().equalsIgnoreCase(protocoltype)) {
 +            final String poolid = storageHost + ":" + mountpoint;
 +            final String namelable = mountpoint;
 +            final String volumedesc = storageHost + ":" + mountpoint;
 +
 +            return getNfsSR(conn, poolid, namelable, storageHost, mountpoint, volumedesc);
 +        } else {
 +            return getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, false, SRType.LVMOISCSI.toString(), true);
 +        }
 +    }
 +
 +    protected VDI prepareManagedStorage(final Connection conn, final Map<String, String> details, final String path, final String vdiNameLabel) throws Exception {
 +        final SR sr = prepareManagedSr(conn, details);
 +
 +        VDI vdi = getVDIbyUuid(conn, path, false);
 +        final Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE));
 +
 +        Set<VDI> vdisInSr = sr.getVDIs(conn);
 +
 +        // If a VDI already exists in the SR (in case we cloned from a template cache), use that.
 +        if (vdisInSr.size() == 1) {
 +            vdi = vdisInSr.iterator().next();
 +        }
 +
 +        if (vdi == null) {
 +            vdi = createVdi(sr, vdiNameLabel, volumeSize);
 +        } else {
 +            // If vdi is not null, it must have already been created, so check whether a resize of the volume was performed.
 +            // If true, resize the VDI to the volume size.
 +
 +            s_logger.info("Checking for the resize of the datadisk");
 +
 +            final long vdiVirtualSize = vdi.getVirtualSize(conn);
 +
 +            if (vdiVirtualSize != volumeSize) {
 +                s_logger.info("Resizing the data disk (VDI) from vdiVirtualSize: " + vdiVirtualSize + " to volumeSize: " + volumeSize);
 +
 +                try {
 +                    vdi.resize(conn, volumeSize);
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to resize volume", e);
 +                }
 +            }
 +
 +            // change the name-label in case of a cloned VDI
 +            if (!Objects.equals(vdi.getNameLabel(conn), vdiNameLabel)) {
 +                try {
 +                    vdi.setNameLabel(conn, vdiNameLabel);
 +                } catch (final Exception e) {
 +                    s_logger.warn("Unable to rename volume", e);
 +                }
 +            }
 +        }
 +
 +        return vdi;
 +    }
 +
 +    protected ExecutionResult prepareNetworkElementCommand(final IpAssocCommand cmd) {
 +        final Connection conn = getConnection();
 +        final String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
 +        final String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
 +
 +        try {
 +            final IpAddressTO[] ips = cmd.getIpAddresses();
 +            for (final IpAddressTO ip : ips) {
 +
 +                final VM router = getVM(conn, routerName);
 +
 +                final NicTO nic = new NicTO();
 +                nic.setMac(ip.getVifMacAddress());
 +                nic.setType(ip.getTrafficType());
 +                if (ip.getBroadcastUri() == null) {
 +                    nic.setBroadcastType(BroadcastDomainType.Native);
 +                } else {
 +                    final URI uri = BroadcastDomainType.fromString(ip.getBroadcastUri());
 +                    nic.setBroadcastType(BroadcastDomainType.getSchemeValue(uri));
 +                    nic.setBroadcastUri(uri);
 +                }
 +                nic.setDeviceId(0);
 +                nic.setNetworkRateMbps(ip.getNetworkRate());
 +                nic.setName(ip.getNetworkName());
 +
 +                final Network network = getNetwork(conn, nic);
 +
 +                // Determine the correct VIF on DomR to associate/disassociate
 +                // the
 +                // IP address with
 +                VIF correctVif = getCorrectVif(conn, router, network);
 +
 +                // If we are associating an IP address and DomR doesn't have a
 +                // VIF
 +                // for the specified vlan ID, we need to add a VIF
 +                // If we are disassociating the last IP address in the VLAN, we
 +                // need
 +                // to remove a VIF
 +                boolean addVif = false;
 +                if (ip.isAdd() && correctVif == null) {
 +                    addVif = true;
 +                }
 +
 +                if (addVif) {
 +                    // Add a new VIF to DomR
 +                    final String vifDeviceNum = getLowestAvailableVIFDeviceNum(conn, router);
 +
 +                    if (vifDeviceNum == null) {
 +                        throw new InternalErrorException("There were no more available slots for a new VIF on router: " + router.getNameLabel(conn));
 +                    }
 +
 +                    nic.setDeviceId(Integer.parseInt(vifDeviceNum));
 +
 +                    correctVif = createVif(conn, routerName, router, null, nic);
 +                    correctVif.plug(conn);
 +                    // Add iptables rule for network usage
 +                    networkUsage(conn, routerIp, "addVif", "eth" + correctVif.getDevice(conn));
 +                }
 +
 +                if (ip.isAdd() && correctVif == null) {
 +                    throw new InternalErrorException("Failed to find DomR VIF to associate/disassociate IP with.");
 +                }
 +                if (correctVif != null) {
 +                    ip.setNicDevId(Integer.valueOf(correctVif.getDevice(conn)));
 +                    ip.setNewNic(addVif);
 +                }
 +            }
 +        } catch (final InternalErrorException e) {
 +            s_logger.error("Ip Assoc failure on applying one ip due to exception:  ", e);
 +            return new ExecutionResult(false, e.getMessage());
 +        } catch (final Exception e) {
 +            return new ExecutionResult(false, e.getMessage());
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    protected ExecutionResult prepareNetworkElementCommand(final IpAssocVpcCommand cmd) {
 +        final Connection conn = getConnection();
 +        final String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
 +        try {
 +            final IpAddressTO[] ips = cmd.getIpAddresses();
 +            for (final IpAddressTO ip : ips) {
 +
 +                final VM router = getVM(conn, routerName);
 +
 +                final VIF correctVif = getVifByMac(conn, router, ip.getVifMacAddress());
 +                setNicDevIdIfCorrectVifIsNotNull(conn, ip, correctVif);
 +            }
 +        } catch (final Exception e) {
 +            s_logger.error("Ip Assoc failure on applying one ip due to exception:  ", e);
 +            return new ExecutionResult(false, e.getMessage());
 +        }
 +
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    protected ExecutionResult prepareNetworkElementCommand(final SetNetworkACLCommand cmd) {
 +        final Connection conn = getConnection();
 +        final String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
 +
 +        try {
 +            final VM router = getVM(conn, routerName);
 +
 +            final NicTO nic = cmd.getNic();
 +            if (nic != null) {
 +                final VIF vif = getVifByMac(conn, router, nic.getMac());
 +                if (vif == null) {
 +                    final String msg = "Prepare SetNetworkACL failed due to VIF is null for : " + nic.getMac() + " with routername: " + routerName;
 +                    s_logger.error(msg);
 +                    return new ExecutionResult(false, msg);
 +                }
 +                nic.setDeviceId(Integer.parseInt(vif.getDevice(conn)));
 +            } else {
 +                final String msg = "Prepare SetNetworkACL failed due to nic is null for : " + routerName;
 +                s_logger.error(msg);
 +                return new ExecutionResult(false, msg);
 +            }
 +        } catch (final Exception e) {
 +            final String msg = "Prepare SetNetworkACL failed due to " + e.toString();
 +            s_logger.error(msg, e);
 +            return new ExecutionResult(false, msg);
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    protected ExecutionResult prepareNetworkElementCommand(final SetSourceNatCommand cmd) {
 +        final Connection conn = getConnection();
 +        final String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
 +        final IpAddressTO pubIp = cmd.getIpAddress();
 +        try {
 +            final VM router = getVM(conn, routerName);
 +
 +            final VIF correctVif = getCorrectVif(conn, router, pubIp);
 +
 +            pubIp.setNicDevId(Integer.valueOf(correctVif.getDevice(conn)));
 +
 +        } catch (final Exception e) {
 +            final String msg = "Ip SNAT failure due to " + e.toString();
 +            s_logger.error(msg, e);
 +            return new ExecutionResult(false, msg);
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    /**
 +     * @param cmd
 +     * @return
 +     */
 +    private ExecutionResult prepareNetworkElementCommand(final SetupGuestNetworkCommand cmd) {
 +        final Connection conn = getConnection();
 +        final NicTO nic = cmd.getNic();
 +        final String domrName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
 +        try {
 +            final Set<VM> vms = VM.getByNameLabel(conn, domrName);
 +            if (vms == null || vms.isEmpty()) {
 +                return new ExecutionResult(false, "Can not find VM " + domrName);
 +            }
 +            final VM vm = vms.iterator().next();
 +            final String mac = nic.getMac();
 +            VIF domrVif = null;
 +            for (final VIF vif : vm.getVIFs(conn)) {
 +                final String lmac = vif.getMAC(conn);
 +                if (lmac.equals(mac)) {
 +                    domrVif = vif;
 +                    // Do not break it! We have 2 routers.
 +                    // break;
 +                }
 +            }
 +            if (domrVif == null) {
 +                return new ExecutionResult(false, "Can not find vif with mac " + mac + " for VM " + domrName);
 +            }
 +
 +            nic.setDeviceId(Integer.parseInt(domrVif.getDevice(conn)));
 +        } catch (final Exception e) {
 +            final String msg = "Creating guest network failed due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            return new ExecutionResult(false, msg);
 +        }
 +        return new ExecutionResult(true, null);
 +    }
 +
 +    public void rebootVM(final Connection conn, final VM vm, final String vmName) throws Exception {
 +        Task task = null;
 +        try {
 +            task = vm.cleanRebootAsync(conn);
 +            try {
 +                // poll every 1 seconds , timeout after 10 minutes
 +                waitForTask(conn, task, 1000, 10 * 60 * 1000);
 +                checkForSuccess(conn, task);
 +            } catch (final Types.HandleInvalid e) {
 +                if (vm.getPowerState(conn) == VmPowerState.RUNNING) {
 +                    task = null;
 +                    return;
 +                }
 +                throw new CloudRuntimeException("Reboot VM catch HandleInvalid and VM is not in RUNNING state");
 +            }
 +        } catch (final XenAPIException e) {
 +            s_logger.debug("Unable to Clean Reboot VM(" + vmName + ") on host(" + _host.getUuid() + ") due to " + e.toString() + ", try hard reboot");
 +            try {
 +                vm.hardReboot(conn);
 +            } catch (final Exception e1) {
 +                final String msg = "Unable to hard Reboot VM(" + vmName + ") on host(" + _host.getUuid() + ") due to " + e.toString();
 +                s_logger.warn(msg, e1);
 +                throw new CloudRuntimeException(msg);
 +            }
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e1) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e1.toString());
 +                }
 +            }
 +        }
 +    }
 +
 +    protected void skipOrRemoveSR(Connection conn, SR sr) {
 +        if (sr == null) {
 +            return;
 +        }
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug(logX(sr, "Removing SR"));
 +        }
 +        try {
 +            Set<VDI> vdis = sr.getVDIs(conn);
 +            for (VDI vdi : vdis) {
 +                if (MapUtils.isEmpty(vdi.getCurrentOperations(conn))) {
 +                    continue;
 +                }
 +                return;
 +            }
 +            removeSR(conn, sr);
 +            return;
 +        } catch (XenAPIException | XmlRpcException e) {
 +            s_logger.warn(logX(sr, "Unable to get current opertions " + e.toString()), e);
 +        }
 +        String msg = "Remove SR failed";
 +        s_logger.warn(msg);
 +    }
 +
 +    public void removeSR(final Connection conn, final SR sr) {
 +        if (sr == null) {
 +            return;
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug(logX(sr, "Removing SR"));
 +        }
 +
 +        for (int i = 0; i < 2; i++) {
 +            try {
 +                final Set<VDI> vdis = sr.getVDIs(conn);
 +                for (final VDI vdi : vdis) {
 +                    vdi.forget(conn);
 +                }
 +
 +                Set<PBD> pbds = sr.getPBDs(conn);
 +                for (final PBD pbd : pbds) {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug(logX(pbd, "Unplugging pbd"));
 +                    }
 +
 +                    // if (pbd.getCurrentlyAttached(conn)) {
 +                    pbd.unplug(conn);
 +                    // }
 +
 +                    pbd.destroy(conn);
 +                }
 +
 +                pbds = sr.getPBDs(conn);
 +
 +                if (pbds.size() == 0) {
 +                    if (s_logger.isDebugEnabled()) {
 +                        s_logger.debug(logX(sr, "Forgetting"));
 +                    }
 +
 +                    sr.forget(conn);
 +
 +                    return;
 +                }
 +
 +                if (s_logger.isDebugEnabled()) {
 +                    s_logger.debug(logX(sr, "There is still one or more PBDs attached."));
 +
 +                    if (s_logger.isTraceEnabled()) {
 +                        for (final PBD pbd : pbds) {
 +                            s_logger.trace(logX(pbd, " Still attached"));
 +                        }
 +                    }
 +                }
 +            } catch (final XenAPIException e) {
 +                s_logger.debug(logX(sr, "Catch XenAPIException: " + e.toString()));
 +            } catch (final XmlRpcException e) {
 +                s_logger.debug(logX(sr, "Catch Exception: " + e.getMessage()));
 +            }
 +        }
 +
 +        s_logger.warn(logX(sr, "Unable to remove SR"));
 +    }
 +
 +    protected String removeSRSync(final Connection conn, final SR sr) {
 +        if (sr == null) {
 +            return null;
 +        }
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug(logX(sr, "Removing SR"));
 +        }
 +        long waittime = 0;
 +        try {
 +            final Set<VDI> vdis = sr.getVDIs(conn);
 +            for (final VDI vdi : vdis) {
 +                final Map<java.lang.String, Types.VdiOperations> currentOperation = vdi.getCurrentOperations(conn);
 +                if (currentOperation == null || currentOperation.size() == 0) {
 +                    continue;
 +                }
 +                if (waittime >= 1800000) {
 +                    final String msg = "This template is being used, try late time";
 +                    s_logger.warn(msg);
 +                    return msg;
 +                }
 +                waittime += 30000;
 +                try {
 +                    Thread.sleep(30000);
 +                } catch (final InterruptedException ex) {
 +                }
 +            }
 +            removeSR(conn, sr);
 +            return null;
 +        } catch (final XenAPIException e) {
 +            s_logger.warn(logX(sr, "Unable to get current opertions " + e.toString()), e);
 +        } catch (final XmlRpcException e) {
 +            s_logger.warn(logX(sr, "Unable to get current opertions " + e.getMessage()), e);
 +        }
 +        final String msg = "Remove SR failed";
 +        s_logger.warn(msg);
 +        return msg;
 +
 +    }
 +
 +    public String revertToSnapshot(final Connection conn, final VM vmSnapshot, final String vmName, final String oldVmUuid, final Boolean snapshotMemory, final String hostUUID)
 +            throws XenAPIException, XmlRpcException {
 +
 +        final String results = callHostPluginAsync(conn, "vmopsSnapshot", "revert_memory_snapshot", 10 * 60 * 1000, "snapshotUUID", vmSnapshot.getUuid(conn), "vmName", vmName, "oldVmUuid", oldVmUuid,
 +                "snapshotMemory", snapshotMemory.toString(), "hostUUID", hostUUID);
 +        String errMsg = null;
 +        if (results == null || results.isEmpty()) {
 +            errMsg = "revert_memory_snapshot return null";
 +        } else {
 +            if (results.equals("0")) {
 +                return results;
 +            } else {
 +                errMsg = "revert_memory_snapshot exception";
 +            }
 +        }
 +        s_logger.warn(errMsg);
 +        throw new CloudRuntimeException(errMsg);
 +    }
 +
 +    public void scaleVM(final Connection conn, final VM vm, final VirtualMachineTO vmSpec, final Host host) throws XenAPIException, XmlRpcException {
 +
 +        final Long staticMemoryMax = vm.getMemoryStaticMax(conn);
 +        final Long staticMemoryMin = vm.getMemoryStaticMin(conn);
 +        final Long newDynamicMemoryMin = vmSpec.getMinRam();
 +        final Long newDynamicMemoryMax = vmSpec.getMaxRam();
 +        if (staticMemoryMin > newDynamicMemoryMin || newDynamicMemoryMax > staticMemoryMax) {
 +            throw new CloudRuntimeException("Cannot scale up the vm because of memory constraint violation: " + "0 <= memory-static-min(" + staticMemoryMin + ") <= memory-dynamic-min("
 +                    + newDynamicMemoryMin + ") <= memory-dynamic-max(" + newDynamicMemoryMax + ") <= memory-static-max(" + staticMemoryMax + ")");
 +        }
 +
 +        vm.setMemoryDynamicRange(conn, newDynamicMemoryMin, newDynamicMemoryMax);
 +        vm.setVCPUsNumberLive(conn, (long)vmSpec.getCpus());
 +
 +        final Integer speed = vmSpec.getMinSpeed();
 +        if (speed != null) {
 +
 +            int cpuWeight = _maxWeight; // cpu_weight
 +
 +            // weight based allocation
 +
 +            cpuWeight = (int)(speed * 0.99 / _host.getSpeed() * _maxWeight);
 +            if (cpuWeight > _maxWeight) {
 +                cpuWeight = _maxWeight;
 +            }
 +
 +            if (vmSpec.getLimitCpuUse()) {
 +                long utilization = 0; // max CPU cap, default is unlimited
 +                utilization = (int)(vmSpec.getMaxSpeed() * 0.99 * vmSpec.getCpus() / _host.getSpeed() * 100);
 +                // vm.addToVCPUsParamsLive(conn, "cap",
 +                // Long.toString(utilization)); currently xenserver doesnot
 +                // support Xapi to add VCPUs params live.
 +                callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "cap", "value", Long.toString(utilization), "vmname", vmSpec.getName());
 +            }
 +            // vm.addToVCPUsParamsLive(conn, "weight",
 +            // Integer.toString(cpuWeight));
 +            callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", Integer.toString(cpuWeight), "vmname", vmSpec.getName());
 +        }
 +    }
 +
 +    @Override
 +    public void setAgentControl(final IAgentControl agentControl) {
 +        _agentControl = agentControl;
 +    }
 +
 +    public void setCanBridgeFirewall(final boolean canBridgeFirewall) {
 +        _canBridgeFirewall = canBridgeFirewall;
 +    }
 +
 +    @Override
 +    public void setConfigParams(final Map<String, Object> params) {
 +    }
 +
 +    public boolean setIptables(final Connection conn) {
 +        final String result = callHostPlugin(conn, "vmops", "setIptables");
 +        if (result == null || result.isEmpty()) {
 +            return false;
 +        }
 +        return true;
 +    }
 +
 +    public void setIsOvs(final boolean isOvs) {
 +        _isOvs = isOvs;
 +    }
 +
 +    /**
 +     * WARN: static-min <= dynamic-min <= dynamic-max <= static-max
 +     *
 +     * @see XcpServerResource#setMemory(com.xensource.xenapi.Connection,
 +     *      com.xensource.xenapi.VM, long, long)
 +     * @param conn
 +     * @param vm
 +     * @param minMemsize
 +     * @param maxMemsize
 +     * @throws XmlRpcException
 +     * @throws XenAPIException
 +     */
 +    protected void setMemory(final Connection conn, final VM vm, final long minMemsize, final long maxMemsize) throws XmlRpcException, XenAPIException {
 +        vm.setMemoryLimits(conn, mem_128m, maxMemsize, minMemsize, maxMemsize);
 +    }
 +
 +    @Override
 +    public void setName(final String name) {
 +    }
 +
 +    protected void setNicDevIdIfCorrectVifIsNotNull(final Connection conn, final IpAddressTO ip, final VIF correctVif)
 +            throws InternalErrorException, BadServerResponse, XenAPIException, XmlRpcException {
 +        if (correctVif == null) {
 +            if (ip.isAdd()) {
 +                throw new InternalErrorException("Failed to find DomR VIF to associate IP with.");
 +            } else {
 +                s_logger.debug("VIF to deassociate IP with does not exist, return success");
 +            }
 +        } else {
 +            ip.setNicDevId(Integer.valueOf(correctVif.getDevice(conn)));
 +        }
 +    }
 +
 +    @Override
 +    public void setRunLevel(final int level) {
 +    }
 +
 +    public String setupHeartbeatSr(final Connection conn, final SR sr, final boolean force) throws XenAPIException, XmlRpcException {
 +        final SR.Record srRec = sr.getRecord(conn);
 +        final String srUuid = srRec.uuid;
 +        if (!srRec.shared || !SRType.LVMOHBA.equals(srRec.type) && !SRType.LVMOISCSI.equals(srRec.type) && !SRType.NFS.equals(srRec.type)) {
 +            return srUuid;
 +        }
 +        String result = null;
 +        final Host host = Host.getByUuid(conn, _host.getUuid());
 +        final Set<String> tags = host.getTags(conn);
 +        if (force || !tags.contains("cloud-heartbeat-" + srUuid)) {
 +            if (s_logger.isDebugEnabled()) {
 +                s_logger.debug("Setting up the heartbeat sr for host " + _host.getIp() + " and sr " + srUuid);
 +            }
 +            final Set<PBD> pbds = sr.getPBDs(conn);
 +            for (final PBD pbd : pbds) {
 +                final PBD.Record pbdr = pbd.getRecord(conn);
 +                if (!pbdr.currentlyAttached && pbdr.host.getUuid(conn).equals(_host.getUuid())) {
 +                    pbd.plug(conn);
 +                    break;
 +                }
 +            }
 +            result = callHostPluginThroughMaster(conn, "vmopspremium", "setup_heartbeat_sr", "host", _host.getUuid(), "sr", srUuid);
 +            if (result == null || !result.split("#")[1].equals("0")) {
 +                throw new CloudRuntimeException("Unable to setup heartbeat sr on SR " + srUuid + " due to " + result);
 +            }
 +
 +            if (!tags.contains("cloud-heartbeat-" + srUuid)) {
 +                tags.add("cloud-heartbeat-" + srUuid);
 +                host.setTags(conn, tags);
 +            }
 +        }
 +        result = callHostPluginPremium(conn, "setup_heartbeat_file", "host", _host.getUuid(), "sr", srUuid, "add", "true");
 +        if (result == null || !result.split("#")[1].equals("0")) {
 +            throw new CloudRuntimeException("Unable to setup heartbeat file entry on SR " + srUuid + " due to " + result);
 +        }
 +        return srUuid;
 +    }
 +
 +    public void setupLinkLocalNetwork(final Connection conn) {
 +        try {
 +            final Network.Record rec = new Network.Record();
 +            final Set<Network> networks = Network.getByNameLabel(conn, _linkLocalPrivateNetworkName);
 +            Network linkLocal = null;
 +
 +            if (networks.size() == 0) {
 +                rec.nameDescription = "link local network used by system vms";
 +                rec.nameLabel = _linkLocalPrivateNetworkName;
 +                final Map<String, String> configs = new HashMap<String, String>();
 +                configs.put("ip_begin", NetUtils.getLinkLocalGateway());
 +                configs.put("ip_end", NetUtils.getLinkLocalIpEnd());
 +                configs.put("netmask", NetUtils.getLinkLocalNetMask());
 +                configs.put("vswitch-disable-in-band", "true");
 +                rec.otherConfig = configs;
 +                linkLocal = Network.create(conn, rec);
 +            } else {
 +                linkLocal = networks.iterator().next();
 +                if (!linkLocal.getOtherConfig(conn).containsKey("vswitch-disable-in-band")) {
 +                    linkLocal.addToOtherConfig(conn, "vswitch-disable-in-band", "true");
 +                }
 +            }
 +
 +            /* Make sure there is a physical bridge on this network */
 +            VIF dom0vif = null;
 +            final Pair<VM, VM.Record> vm = getControlDomain(conn);
 +            final VM dom0 = vm.first();
 +            final Set<VIF> vifs = dom0.getVIFs(conn);
 +            if (vifs.size() != 0) {
 +                for (final VIF vif : vifs) {
 +                    final Map<String, String> otherConfig = vif.getOtherConfig(conn);
 +                    if (otherConfig != null) {
 +                        final String nameLabel = otherConfig.get("nameLabel");
 +                        if (nameLabel != null && nameLabel.equalsIgnoreCase("link_local_network_vif")) {
 +                            dom0vif = vif;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            /* create temp VIF0 */
 +            if (dom0vif == null) {
 +                s_logger.debug("Can't find a vif on dom0 for link local, creating a new one");
 +                final VIF.Record vifr = new VIF.Record();
 +                vifr.VM = dom0;
 +                vifr.device = getLowestAvailableVIFDeviceNum(conn, dom0);
 +                if (vifr.device == null) {
 +                    s_logger.debug("Failed to create link local network, no vif available");
 +                    return;
 +                }
 +                final Map<String, String> config = new HashMap<String, String>();
 +                config.put("nameLabel", "link_local_network_vif");
 +                vifr.otherConfig = config;
 +                vifr.MAC = "FE:FF:FF:FF:FF:FF";
 +                vifr.network = linkLocal;
 +                vifr.lockingMode = Types.VifLockingMode.NETWORK_DEFAULT;
 +                dom0vif = VIF.create(conn, vifr);
 +                plugDom0Vif(conn, dom0vif);
 +            } else {
 +                s_logger.debug("already have a vif on dom0 for link local network");
 +                if (!dom0vif.getCurrentlyAttached(conn)) {
 +                    plugDom0Vif(conn, dom0vif);
 +                }
 +            }
 +
 +            final String brName = linkLocal.getBridge(conn);
 +            callHostPlugin(conn, "vmops", "setLinkLocalIP", "brName", brName);
 +            _host.setLinkLocalNetwork(linkLocal.getUuid(conn));
 +
 +        } catch (final XenAPIException e) {
 +            s_logger.warn("Unable to create local link network", e);
 +            throw new CloudRuntimeException("Unable to create local link network due to " + e.toString(), e);
 +        } catch (final XmlRpcException e) {
 +            s_logger.warn("Unable to create local link network", e);
 +            throw new CloudRuntimeException("Unable to create local link network due to " + e.toString(), e);
 +        }
 +    }
 +
 +    /* return : if setup is needed */
 +    public boolean setupServer(final Connection conn, final Host host) {
 +        final String packageVersion = CitrixResourceBase.class.getPackage().getImplementationVersion();
 +        final String version = this.getClass().getName() + "-" + (packageVersion == null ? Long.toString(System.currentTimeMillis()) : packageVersion);
 +
 +        try {
 +            /* push patches to XenServer */
 +            final Host.Record hr = host.getRecord(conn);
 +
 +            final Iterator<String> it = hr.tags.iterator();
 +
 +            while (it.hasNext()) {
 +                final String tag = it.next();
 +                if (tag.startsWith("vmops-version-")) {
 +                    if (tag.contains(version)) {
 +                        s_logger.info(logX(host, "Host " + hr.address + " is already setup."));
 +                        return false;
 +                    } else {
 +                        it.remove();
 +                    }
 +                }
 +            }
 +
 +            final com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(hr.address, 22);
 +            try {
 +                sshConnection.connect(null, 60000, 60000);
 +                if (!sshConnection.authenticateWithPassword(_username, _password.peek())) {
 +                    throw new CloudRuntimeException("Unable to authenticate");
 +                }
 +
 +                final String cmd = "mkdir -p /opt/cloud/bin /var/log/cloud";
 +                if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) {
 +                    throw new CloudRuntimeException("Cannot create directory /opt/cloud/bin on XenServer hosts");
 +                }
 +
 +                final SCPClient scp = new SCPClient(sshConnection);
 +
 +                final List<File> files = getPatchFiles();
 +                if (files == null || files.isEmpty()) {
 +                    throw new CloudRuntimeException("Can not find patch file");
 +                }
 +                for (final File file : files) {
 +                    final String path = file.getParentFile().getAbsolutePath() + "/";
 +                    final Properties props = PropertiesUtil.loadFromFile(file);
 +
 +                    for (final Map.Entry<Object, Object> entry : props.entrySet()) {
 +                        final String k = (String)entry.getKey();
 +                        final String v = (String)entry.getValue();
 +
 +                        assert k != null && k.length() > 0 && v != null && v.length() > 0 : "Problems with " + k + "=" + v;
 +
 +                        final String[] tokens = v.split(",");
 +                        String f = null;
 +                        if (tokens.length == 3 && tokens[0].length() > 0) {
 +                            if (tokens[0].startsWith("/")) {
 +                                f = tokens[0];
 +                            } else if (tokens[0].startsWith("~")) {
 +                                final String homedir = System.getenv("HOME");
 +                                f = homedir + tokens[0].substring(1) + k;
 +                            } else {
 +                                f = path + tokens[0] + '/' + k;
 +                            }
 +                        } else {
 +                            f = path + k;
 +                        }
 +                        final String directoryPath = tokens[tokens.length - 1];
 +
 +                        f = f.replace('/', File.separatorChar);
 +
 +                        String permissions = "0755";
 +                        if (tokens.length == 3) {
 +                            permissions = tokens[1];
 +                        } else if (tokens.length == 2) {
 +                            permissions = tokens[0];
 +                        }
 +
 +                        if (!new File(f).exists()) {
 +                            s_logger.warn("We cannot locate " + f);
 +                            continue;
 +                        }
 +                        if (s_logger.isDebugEnabled()) {
 +                            s_logger.debug("Copying " + f + " to " + directoryPath + " on " + hr.address + " with permission " + permissions);
 +                        }
 +
 +                        if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "mkdir -m 700 -p " + directoryPath)) {
 +                            s_logger.debug("Unable to create destination path: " + directoryPath + " on " + hr.address + ".");
 +                        }
 +
 +                        try {
 +                            scp.put(f, directoryPath, permissions);
 +                        } catch (final IOException e) {
 +                            final String msg = "Unable to copy file " + f + " to path " + directoryPath + " with permissions  " + permissions;
 +                            s_logger.debug(msg);
 +                            throw new CloudRuntimeException("Unable to setup the server: " + msg, e);
 +                        }
 +                    }
 +                }
 +
 +            } catch (final IOException e) {
 +                throw new CloudRuntimeException("Unable to setup the server correctly", e);
 +            } finally {
 +                sshConnection.close();
 +            }
 +            hr.tags.add("vmops-version-" + version);
 +            host.setTags(conn, hr.tags);
 +            return true;
 +        } catch (final XenAPIException e) {
 +            final String msg = "XenServer setup failed due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException("Unable to get host information " + e.toString(), e);
 +        } catch (final XmlRpcException e) {
 +            final String msg = "XenServer setup failed due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException("Unable to get host information ", e);
 +        }
 +    }
 +
 +    public synchronized Network setupvSwitchNetwork(final Connection conn) {
 +        try {
 +            if (_host.getVswitchNetwork() == null) {
 +                Network vswitchNw = null;
 +                final Network.Record rec = new Network.Record();
 +                final String nwName = Networks.BroadcastScheme.VSwitch.toString();
 +                final Set<Network> networks = Network.getByNameLabel(conn, nwName);
 +
 +                if (networks.size() == 0) {
 +                    rec.nameDescription = "vswitch network for " + nwName;
 +                    rec.nameLabel = nwName;
 +                    vswitchNw = Network.create(conn, rec);
 +                } else {
 +                    vswitchNw = networks.iterator().next();
 +                }
 +                _host.setVswitchNetwork(vswitchNw);
 +            }
 +            return _host.getVswitchNetwork();
 +        } catch (final BadServerResponse e) {
 +            s_logger.error("Failed to setup vswitch network", e);
 +        } catch (final XenAPIException e) {
 +            s_logger.error("Failed to setup vswitch network", e);
 +        } catch (final XmlRpcException e) {
 +            s_logger.error("Failed to setup vswitch network", e);
 +        }
 +
 +        return null;
 +    }
 +
 +    public void shutdownVM(final Connection conn, final VM vm, final String vmName, final boolean forcedStop) throws XmlRpcException {
 +        Task task = null;
 +        try {
 +            if (forcedStop) {
 +                task = vm.hardShutdownAsync(conn);
 +            } else {
 +                task = vm.cleanShutdownAsync(conn);
 +            }
 +
 +            try {
 +                // poll every 1 seconds , timeout after 10 minutes
 +                waitForTask(conn, task, 1000, 10 * 60 * 1000);
 +                checkForSuccess(conn, task);
 +            } catch (final TimeoutException e) {
 +                if (vm.getPowerState(conn) == VmPowerState.HALTED) {
 +                    task = null;
 +                    return;
 +                }
 +                throw new CloudRuntimeException("Shutdown VM catch HandleInvalid and VM is not in HALTED state");
 +            }
 +        } catch (final XenAPIException e) {
 +            s_logger.debug("Unable to shutdown VM(" + vmName + ") with force=" + forcedStop + " on host(" + _host.getUuid() + ") due to " + e.toString());
 +            try {
 +                VmPowerState state = vm.getPowerState(conn);
 +                if (state == VmPowerState.RUNNING) {
 +                    try {
 +                        vm.hardShutdown(conn);
 +                    } catch (final Exception e1) {
 +                        s_logger.debug("Unable to hardShutdown VM(" + vmName + ") on host(" + _host.getUuid() + ") due to " + e.toString());
 +                        state = vm.getPowerState(conn);
 +                        if (state == VmPowerState.RUNNING) {
 +                            forceShutdownVM(conn, vm);
 +                        }
 +                        return;
 +                    }
 +                } else if (state == VmPowerState.HALTED) {
 +                    return;
 +                } else {
 +                    final String msg = "After cleanShutdown the VM status is " + state.toString() + ", that is not expected";
 +                    s_logger.warn(msg);
 +                    throw new CloudRuntimeException(msg);
 +                }
 +            } catch (final Exception e1) {
 +                final String msg = "Unable to hardShutdown VM(" + vmName + ") on host(" + _host.getUuid() + ") due to " + e.toString();
 +                s_logger.warn(msg, e1);
 +                throw new CloudRuntimeException(msg);
 +            }
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e1) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e1.toString());
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean start() {
 +        return true;
 +    }
 +
 +    public void startVM(final Connection conn, final Host host, final VM vm, final String vmName) throws Exception {
 +        Task task = null;
 +        try {
 +            task = vm.startOnAsync(conn, host, false, true);
 +            try {
 +                // poll every 1 seconds , timeout after 10 minutes
 +                waitForTask(conn, task, 1000, 10 * 60 * 1000);
 +                checkForSuccess(conn, task);
 +            } catch (final Types.HandleInvalid e) {
 +                if (vm.getPowerState(conn) == VmPowerState.RUNNING) {
 +                    s_logger.debug("VM " + vmName + " is in Running status");
 +                    task = null;
 +                    return;
 +                }
 +                throw new CloudRuntimeException("Start VM " + vmName + " catch HandleInvalid and VM is not in RUNNING state");
 +            } catch (final TimeoutException e) {
 +                if (vm.getPowerState(conn) == VmPowerState.RUNNING) {
 +                    s_logger.debug("VM " + vmName + " is in Running status");
 +                    task = null;
 +                    return;
 +                }
 +                throw new CloudRuntimeException("Start VM " + vmName + " catch BadAsyncResult and VM is not in RUNNING state");
 +            }
 +        } catch (final XenAPIException e) {
 +            final String msg = "Unable to start VM(" + vmName + ") on host(" + _host.getUuid() + ") due to " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg);
 +        } finally {
 +            if (task != null) {
 +                try {
 +                    task.destroy(conn);
 +                } catch (final Exception e1) {
 +                    s_logger.debug("unable to destroy task(" + task.toString() + ") on host(" + _host.getUuid() + ") due to " + e1.toString());
 +                }
 +            }
 +        }
 +    }
 +
 +    protected void startvmfailhandle(final Connection conn, final VM vm, final List<Ternary<SR, VDI, VolumeVO>> mounts) {
 +        if (vm != null) {
 +            try {
 +
 +                if (vm.getPowerState(conn) == VmPowerState.RUNNING) {
 +                    try {
 +                        vm.hardShutdown(conn);
 +                    } catch (final Exception e) {
 +                        final String msg = "VM hardshutdown failed due to " + e.toString();
 +                        s_logger.warn(msg, e);
 +                    }
 +                }
 +                if (vm.getPowerState(conn) == VmPowerState.HALTED) {
 +                    try {
 +                        vm.destroy(conn);
 +                    } catch (final Exception e) {
 +                        final String msg = "VM destroy failed due to " + e.toString();
 +                        s_logger.warn(msg, e);
 +                    }
 +                }
 +            } catch (final Exception e) {
 +                final String msg = "VM getPowerState failed due to " + e.toString();
 +                s_logger.warn(msg, e);
 +            }
 +        }
 +        if (mounts != null) {
 +            for (final Ternary<SR, VDI, VolumeVO> mount : mounts) {
 +                final VDI vdi = mount.second();
 +                Set<VBD> vbds = null;
 +                try {
 +                    vbds = vdi.getVBDs(conn);
 +                } catch (final Exception e) {
 +                    final String msg = "VDI getVBDS failed due to " + e.toString();
 +                    s_logger.warn(msg, e);
 +                    continue;
 +                }
 +                for (final VBD vbd : vbds) {
 +                    try {
 +                        vbd.unplug(conn);
 +                        vbd.destroy(conn);
 +                    } catch (final Exception e) {
 +                        final String msg = "VBD destroy failed due to " + e.toString();
 +                        s_logger.warn(msg, e);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public boolean stop() {
 +        disconnected();
 +        return true;
 +    }
 +
 +    private HashMap<String, Pair<Long, Long>> syncNetworkGroups(final Connection conn, final long id) {
 +        final HashMap<String, Pair<Long, Long>> states = new HashMap<String, Pair<Long, Long>>();
 +
 +        final String result = callHostPlugin(conn, "vmops", "get_rule_logs_for_vms", "host_uuid", _host.getUuid());
 +        s_logger.trace("syncNetworkGroups: id=" + id + " got: " + result);
 +        final String[] rulelogs = result != null ? result.split(";") : new String[0];
 +        for (final String rulesforvm : rulelogs) {
 +            final String[] log = rulesforvm.split(",");
 +            if (log.length != 6) {
 +                continue;
 +            }
 +            // output = ','.join([vmName, vmID, vmIP, domID, signature, seqno])
 +            try {
 +                states.put(log[0], new Pair<Long, Long>(Long.parseLong(log[1]), Long.parseLong(log[5])));
 +            } catch (final NumberFormatException nfe) {
 +                states.put(log[0], new Pair<Long, Long>(-1L, -1L));
 +            }
 +        }
 +        return states;
 +    }
 +
 +    public boolean transferManagementNetwork(final Connection conn, final Host host, final PIF src, final PIF.Record spr, final PIF dest) throws XmlRpcException, XenAPIException {
 +        dest.reconfigureIp(conn, spr.ipConfigurationMode, spr.IP, spr.netmask, spr.gateway, spr.DNS);
 +        Host.managementReconfigure(conn, dest);
 +        String hostUuid = null;
 +        int count = 0;
 +        while (count < 10) {
 +            try {
 +                Thread.sleep(10000);
 +                hostUuid = host.getUuid(conn);
 +                if (hostUuid != null) {
 +                    break;
 +                }
 +                ++count;
 +            } catch (final XmlRpcException e) {
 +                s_logger.debug("Waiting for host to come back: " + e.getMessage());
 +            } catch (final XenAPIException e) {
 +                s_logger.debug("Waiting for host to come back: " + e.getMessage());
 +            } catch (final InterruptedException e) {
 +                s_logger.debug("Gotta run");
 +                return false;
 +            }
 +        }
 +        if (hostUuid == null) {
 +            s_logger.warn("Unable to transfer the management network from " + spr.uuid);
 +            return false;
 +        }
 +
 +        src.reconfigureIp(conn, Types.IpConfigurationMode.NONE, null, null, null, null);
 +        return true;
 +    }
 +
 +    protected void umount(final Connection conn, final VDI vdi) {
 +
 +    }
 +
 +    public void umountSnapshotDir(final Connection conn, final Long dcId) {
 +        try {
 +            callHostPlugin(conn, "vmopsSnapshot", "unmountSnapshotsDir", "dcId", dcId.toString());
 +        } catch (final Exception e) {
 +            s_logger.debug("Failed to umount snapshot dir", e);
 +        }
 +    }
 +
 +    public String upgradeSnapshot(final Connection conn, final String templatePath, final String snapshotPath) {
 +        final String results = callHostPluginAsync(conn, "vmopspremium", "upgrade_snapshot", 2 * 60 * 60, "templatePath", templatePath, "snapshotPath", snapshotPath);
 +
 +        if (results == null || results.isEmpty()) {
 +            final String msg = "upgrade_snapshot return null";
 +            s_logger.warn(msg);
 +            throw new CloudRuntimeException(msg);
 +        }
 +        final String[] tmp = results.split("#");
 +        final String status = tmp[0];
 +        if (status.equals("0")) {
 +            return results;
 +        } else {
 +            s_logger.warn(results);
 +            throw new CloudRuntimeException(results);
 +        }
 +    }
 +
 +    public void waitForTask(final Connection c, final Task task, final long pollInterval, final long timeout) throws XenAPIException, XmlRpcException, TimeoutException {
 +        final long beginTime = System.currentTimeMillis();
 +        if (s_logger.isTraceEnabled()) {
 +            s_logger.trace("Task " + task.getNameLabel(c) + " (" + task.getUuid(c) + ") sent to " + c.getSessionReference() + " is pending completion with a " + timeout + "ms timeout");
 +        }
 +        while (task.getStatus(c) == Types.TaskStatusType.PENDING) {
 +            try {
 +                if (s_logger.isTraceEnabled()) {
 +                    s_logger.trace("Task " + task.getNameLabel(c) + " (" + task.getUuid(c) + ") is pending, sleeping for " + pollInterval + "ms");
 +                }
 +                Thread.sleep(pollInterval);
 +            } catch (final InterruptedException e) {
 +            }
 +            if (System.currentTimeMillis() - beginTime > timeout) {
 +                final String msg = "Async " + timeout / 1000 + " seconds timeout for task " + task.toString();
 +                s_logger.warn(msg);
 +                task.cancel(c);
 +                task.destroy(c);
 +                throw new TimeoutException(msg);
 +            }
 +        }
 +    }
 +
 +    public boolean createAndAttachConfigDriveIsoForVM(final Connection conn, final VM vm, final List<String[]> vmDataList, final String configDriveLabel) throws XenAPIException, XmlRpcException {
 +
 +        final String vmName = vm.getNameLabel(conn);
 +
 +        // create SR
 +        final SR sr = createLocalIsoSR(conn, _configDriveSRName + _host.getIp());
 +        if (sr == null) {
 +            s_logger.debug("Failed to create local SR for the config drive");
 +            return false;
 +        }
 +
 +        s_logger.debug("Creating vm data files in config drive for vm " + vmName);
 +        // 1. create vm data files
 +        if (!createVmdataFiles(vmName, vmDataList, configDriveLabel)) {
 +            s_logger.debug("Failed to create vm data files in config drive for vm " + vmName);
 +            return false;
 +        }
 +
 +        // 2. copy config drive iso to host
 +        if (!copyConfigDriveIsoToHost(conn, sr, vmName)) {
 +            return false;
 +        }
 +
 +        // 3. attachIsoToVM
 +        if (!attachConfigDriveIsoToVm(conn, vm)) {
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    public boolean createVmdataFiles(final String vmName, final List<String[]> vmDataList, final String configDriveLabel) {
 +
 +        // add vm iso to the isolibrary
 +        final String isoPath = "/tmp/" + vmName + "/configDrive/";
 +        final String configDriveName = "cloudstack/";
 +
 +        //create folder for the VM
 +        //Remove the folder before creating it.
 +
 +        try {
 +            deleteLocalFolder("/tmp/" + isoPath);
 +        } catch (final IOException e) {
 +            s_logger.debug("Failed to delete the exiting config drive for vm " + vmName + " " + e.getMessage());
 +        } catch (final Exception e) {
 +            s_logger.debug("Failed to delete the exiting config drive for vm " + vmName + " " + e.getMessage());
 +        }
 +
 +        if (vmDataList != null) {
 +            for (final String[] item : vmDataList) {
 +                final String dataType = item[0];
 +                final String fileName = item[1];
 +                final String content = item[2];
 +
 +                // create file with content in folder
 +
 +                if (dataType != null && !dataType.isEmpty()) {
 +                    //create folder
 +                    final String folder = isoPath + configDriveName + dataType;
 +                    if (folder != null && !folder.isEmpty()) {
 +                        final File dir = new File(folder);
 +                        final boolean result = true;
 +
 +                        try {
 +                            if (!dir.exists()) {
 +                                dir.mkdirs();
 +                            }
 +                        } catch (final SecurityException ex) {
 +                            s_logger.debug("Failed to create dir " + ex.getMessage());
 +                            return false;
 +                        }
 +
 +                        if (result && content != null && !content.isEmpty()) {
 +                            File file = new File(folder + "/" + fileName + ".txt");
 +                            try (OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(file.getAbsoluteFile()), "UTF-8");
 +                                    BufferedWriter bw = new BufferedWriter(fw);) {
 +                                bw.write(content);
 +                                s_logger.debug("created file: " + file + " in folder:" + folder);
 +                            } catch (final IOException ex) {
 +                                s_logger.debug("Failed to create file " + ex.getMessage());
 +                                return false;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            s_logger.debug("Created the vm data in " + isoPath);
 +        }
 +
 +        String s = null;
 +        try {
 +
 +            final String cmd = "mkisofs -iso-level 3 -V " + configDriveLabel + " -o " + isoPath + vmName + ".iso " + isoPath;
 +            final Process p = Runtime.getRuntime().exec(cmd);
 +
 +            final BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.defaultCharset()));
 +
 +            final BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream(), Charset.defaultCharset()));
 +
 +            // read the output from the command
 +            while ((s = stdInput.readLine()) != null) {
 +                s_logger.debug(s);
 +            }
 +
 +            // read any errors from the attempted command
 +            while ((s = stdError.readLine()) != null) {
 +                s_logger.debug(s);
 +            }
 +            s_logger.debug(" Created config drive ISO using the command " + cmd + " in the host " + _host.getIp());
 +        } catch (final IOException e) {
 +            s_logger.debug(e.getMessage());
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    public boolean copyConfigDriveIsoToHost(final Connection conn, final SR sr, final String vmName) {
 +
 +        final String vmIso = "/tmp/" + vmName + "/configDrive/" + vmName + ".iso";
 +        //scp file into the host
 +        final com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_host.getIp(), 22);
 +
 +        try {
 +            sshConnection.connect(null, 60000, 60000);
 +            if (!sshConnection.authenticateWithPassword(_username, _password.peek())) {
 +                throw new CloudRuntimeException("Unable to authenticate");
 +            }
 +
 +            s_logger.debug("scp config drive iso file " + vmIso + " to host " + _host.getIp() + " path " + _configDriveIsopath);
 +            final SCPClient scp = new SCPClient(sshConnection);
 +            final String p = "0755";
 +
 +            scp.put(vmIso, _configDriveIsopath, p);
 +            sr.scan(conn);
 +            s_logger.debug("copied config drive iso to host " + _host);
 +        } catch (final IOException e) {
 +            s_logger.debug("failed to copy configdrive iso " + vmIso + " to host " + _host, e);
 +            return false;
 +        } catch (final XmlRpcException e) {
 +            s_logger.debug("Failed to scan config drive iso SR " + _configDriveSRName + _host.getIp() + " in host " + _host, e);
 +            return false;
 +        } finally {
 +            sshConnection.close();
 +            //clean up the config drive files
 +
 +            final String configDir = "/tmp/" + vmName;
 +            try {
 +                deleteLocalFolder(configDir);
 +                s_logger.debug("Successfully cleaned up config drive directory " + configDir + " after copying it to host ");
 +            } catch (final Exception e) {
 +                s_logger.debug("Failed to delete config drive folder :" + configDir + " for VM " + vmName + " " + e.getMessage());
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    public boolean attachConfigDriveIsoToVm(final Connection conn, final VM vm) throws XenAPIException, XmlRpcException {
 +
 +        final String vmName = vm.getNameLabel(conn);
 +        final String isoURL = _configDriveIsopath + vmName + ".iso";
 +        VDI srVdi;
 +
 +        //1. find the vdi of the iso
 +        //2. find the vbd for the vdi
 +        //3. attach iso to vm
 +
 +        try {
 +            final Set<VDI> vdis = VDI.getByNameLabel(conn, vmName + ".iso");
 +            if (vdis.isEmpty()) {
 +                throw new CloudRuntimeException("Could not find ISO with URL: " + isoURL);
 +            }
 +            srVdi = vdis.iterator().next();
 +
 +        } catch (final XenAPIException e) {
 +            s_logger.debug("Unable to get config drive iso: " + isoURL + " due to " + e.toString());
 +            return false;
 +        } catch (final Exception e) {
 +            s_logger.debug("Unable to get config drive iso: " + isoURL + " due to " + e.toString());
 +            return false;
 +        }
 +
 +        VBD isoVBD = null;
 +
 +        // Find the VM's CD-ROM VBD
 +        final Set<VBD> vbds = vm.getVBDs(conn);
 +        for (final VBD vbd : vbds) {
 +            final Types.VbdType type = vbd.getType(conn);
 +
 +            final VBD.Record vbdr = vbd.getRecord(conn);
 +
 +            // if the device exists then attach it
 +            if (!vbdr.userdevice.equals(_attachIsoDeviceNum) && type == Types.VbdType.CD) {
 +                isoVBD = vbd;
 +                break;
 +            }
 +        }
 +
 +        if (isoVBD == null) {
 +            //create vbd
 +            final VBD.Record cfgDriveVbdr = new VBD.Record();
 +            cfgDriveVbdr.VM = vm;
 +            cfgDriveVbdr.empty = true;
 +            cfgDriveVbdr.bootable = false;
 +            cfgDriveVbdr.userdevice = "autodetect";
 +            cfgDriveVbdr.mode = Types.VbdMode.RO;
 +            cfgDriveVbdr.type = Types.VbdType.CD;
 +            final VBD cfgDriveVBD = VBD.create(conn, cfgDriveVbdr);
 +            isoVBD = cfgDriveVBD;
 +
 +            s_logger.debug("Created CD-ROM VBD for VM: " + vm);
 +        }
 +
 +        if (isoVBD != null) {
 +            // If an ISO is already inserted, eject it
 +            if (isoVBD.getEmpty(conn) == false) {
 +                isoVBD.eject(conn);
 +            }
 +
 +            try {
 +                // Insert the new ISO
 +                isoVBD.insert(conn, srVdi);
 +                s_logger.debug("Attached config drive iso to vm " + vmName);
 +            } catch (final XmlRpcException ex) {
 +                s_logger.debug("Failed to attach config drive iso to vm " + vmName);
 +                return false;
 +            }
 +        }
 +
 +        return true;
 +    }
 +
 +    public SR createLocalIsoSR(final Connection conn, final String srName) throws XenAPIException, XmlRpcException {
 +
 +        // if config drive sr already exists then return
 +        SR sr = getSRByNameLabelandHost(conn, _configDriveSRName + _host.getIp());
 +
 +        if (sr != null) {
 +            s_logger.debug("Config drive SR already exist, returing it");
 +            return sr;
 +        }
 +
 +        try {
 +            final Map<String, String> deviceConfig = new HashMap<String, String>();
 +
 +            final com.trilead.ssh2.Connection sshConnection = new com.trilead.ssh2.Connection(_host.getIp(), 22);
 +            try {
 +                sshConnection.connect(null, 60000, 60000);
 +                if (!sshConnection.authenticateWithPassword(_username, _password.peek())) {
 +                    throw new CloudRuntimeException("Unable to authenticate");
 +                }
 +
 +                final String cmd = "mkdir -p " + _configDriveIsopath;
 +                if (!SSHCmdHelper.sshExecuteCmd(sshConnection, cmd)) {
 +                    throw new CloudRuntimeException("Cannot create directory configdrive_iso on XenServer hosts");
 +                }
 +            } catch (final IOException e) {
 +                throw new CloudRuntimeException("Unable to create iso folder", e);
 +            } finally {
 +                sshConnection.close();
 +            }
 +            s_logger.debug("Created the config drive SR " + srName + " folder path " + _configDriveIsopath);
 +
 +            deviceConfig.put("location", _configDriveIsopath);
 +            deviceConfig.put("legacy_mode", "true");
 +            final Host host = Host.getByUuid(conn, _host.getUuid());
 +            final String type = SRType.ISO.toString();
 +            sr = SR.create(conn, host, deviceConfig, new Long(0), _configDriveIsopath, "iso", type, "iso", false, new HashMap<String, String>());
 +
 +            sr.setNameLabel(conn, srName);
 +            sr.setNameDescription(conn, deviceConfig.get("location"));
 +
 +            sr.scan(conn);
 +            s_logger.debug("Config drive ISO SR at the path " + _configDriveIsopath + " got created in host " + _host);
 +            return sr;
 +        } catch (final XenAPIException e) {
 +            final String msg = "createLocalIsoSR failed! mountpoint " + e.toString();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        } catch (final Exception e) {
 +            final String msg = "createLocalIsoSR failed! mountpoint:  due to " + e.getMessage();
 +            s_logger.warn(msg, e);
 +            throw new CloudRuntimeException(msg, e);
 +        }
 +
 +    }
 +
 +    public void deleteLocalFolder(final String directory) throws Exception {
 +        if (directory == null || directory.isEmpty()) {
 +            final String msg = "Invalid directory path (null/empty) detected. Cannot delete specified directory.";
 +            s_logger.debug(msg);
 +            throw new Exception(msg);
 +        }
 +
 +        try {
 +            FileUtils.deleteDirectory(new File(directory));
 +        } catch (final IOException e) {
 +            // IOException here means failure to delete. Not swallowing it here to
 +            // let the caller handle with appropriate contextual log message.
 +            throw e;
 +        }
 +    }
 +
 +    protected SR getSRByNameLabel(Connection conn, String name) throws BadServerResponse, XenAPIException, XmlRpcException {
 +        Set<SR> srs = SR.getByNameLabel(conn, name);
 +        SR ressr = null;
 +        for (SR sr : srs) {
 +            Set<PBD> pbds;
 +            pbds = sr.getPBDs(conn);
 +            for (PBD pbd : pbds) {
 +                PBD.Record pbdr = pbd.getRecord(conn);
 +                if (pbdr.host != null) {
 +                    ressr = sr;
 +                    break;
 +                }
 +            }
 +        }
 +        return ressr;
 +    }
 +
 +    public boolean attachConfigDriveToMigratedVm(Connection conn, String vmName, String ipAddr) {
 +
 +        // attach the config drive in destination host
 +
 +        try {
 +            s_logger.debug("Attaching config drive iso device for the VM " + vmName + " In host " + ipAddr);
 +            Set<VM> vms = VM.getByNameLabel(conn, vmName);
 +
 +            SR sr = getSRByNameLabel(conn, _configDriveSRName + ipAddr);
 +            //Here you will find only two vdis with the <vmname>.iso.
 +            //one is from source host and second from dest host
 +            Set<VDI> vdis = VDI.getByNameLabel(conn, vmName + ".iso");
 +            if (vdis.isEmpty()) {
 +                s_logger.debug("Could not find config drive ISO: " + vmName);
 +                return false;
 +            }
 +
 +            VDI configdriveVdi = null;
 +            for (VDI vdi : vdis) {
 +                SR vdiSr = vdi.getSR(conn);
 +                if (vdiSr.getUuid(conn).equals(sr.getUuid(conn))) {
 +                    //get this vdi to attach to vbd
 +                    configdriveVdi = vdi;
 +                    s_logger.debug("VDI for the config drive ISO  " + vdi);
 +                } else {
 +                    // delete the vdi in source host so that the <vmname>.iso file is get removed
 +                    s_logger.debug("Removing the source host VDI for the config drive ISO  " + vdi);
 +                    vdi.destroy(conn);
 +                }
 +            }
 +
 +            if (configdriveVdi == null) {
 +                s_logger.debug("Config drive ISO VDI is not found ");
 +                return false;
 +            }
 +
 +            for (VM vm : vms) {
 +
 +                //create vbd
 +                VBD.Record cfgDriveVbdr = new VBD.Record();
 +                cfgDriveVbdr.VM = vm;
 +                cfgDriveVbdr.empty = true;
 +                cfgDriveVbdr.bootable = false;
 +                cfgDriveVbdr.userdevice = "autodetect";
 +                cfgDriveVbdr.mode = Types.VbdMode.RO;
 +                cfgDriveVbdr.type = Types.VbdType.CD;
 +
 +                VBD cfgDriveVBD = VBD.create(conn, cfgDriveVbdr);
 +
 +                s_logger.debug("Inserting vbd " + configdriveVdi);
 +                cfgDriveVBD.insert(conn, configdriveVdi);
 +                break;
 +
 +            }
 +
 +            return true;
 +
 +        } catch (BadServerResponse e) {
 +            s_logger.warn("Failed to attach config drive ISO to the VM  " + vmName + " In host " + ipAddr + " due to a bad server response.", e);
 +            return false;
 +        } catch (XenAPIException e) {
 +            s_logger.warn("Failed to attach config drive ISO to the VM  " + vmName + " In host " + ipAddr + " due to a xapi problem.", e);
 +            return false;
 +        } catch (XmlRpcException e) {
 +            s_logger.warn("Failed to attach config drive ISO to the VM  " + vmName + " In host " + ipAddr + " due to a problem in a remote call.", e);
 +            return false;
 +        }
 +
 +    }
 +
 +}

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