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/04/30 06:24:02 UTC

[cloudstack] branch master updated (586edec -> 4277b92)

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

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


    from 586edec  CLOUDSTACK-10253: JSON response for SuccessResponse as boolean instead of string (#2428)
     add 4645512  xenserver: Add support for XS 7.3, 7.4 and XCP-ng 7.4 (#2605)
     new 4277b92  Merge branch '4.11'

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


Summary of changes:
 .../src/main/resources/META-INF/db/schema-41100to41110.sql | 14 +++++++++++++-
 .../xenserver/discoverer/XcpServerDiscoverer.java          |  2 +-
 .../hypervisor/xenserver/resource/CitrixResourceBase.java  | 10 +++++++---
 .../xenserver/resource/XenServerStorageProcessor.java      |  3 ++-
 .../wrapper/xenbase/CitrixAttachIsoCommandWrapper.java     |  2 +-
 .../resource/wrapper/xenbase/XenServerUtilitiesHelper.java |  5 +++++
 systemvm/debian/etc/systemd/system/cloud-postinit.service  |  2 +-
 systemvm/debian/opt/cloud/bin/cs/CsApp.py                  |  2 +-
 systemvm/debian/opt/cloud/bin/cs/CsHelper.py               |  5 +++--
 9 files changed, 34 insertions(+), 11 deletions(-)

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

[cloudstack] 01/01: Merge branch '4.11'

Posted by ro...@apache.org.
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 4277b92abedbdf5c24b46d463947509333bb05b6
Merge: 586edec 4645512
Author: Rohit Yadav <ro...@shapeblue.com>
AuthorDate: Mon Apr 30 08:22:16 2018 +0200

    Merge branch '4.11'
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>

 .../src/main/resources/META-INF/db/schema-41100to41110.sql | 14 +++++++++++++-
 .../xenserver/discoverer/XcpServerDiscoverer.java          |  2 +-
 .../hypervisor/xenserver/resource/CitrixResourceBase.java  | 10 +++++++---
 .../xenserver/resource/XenServerStorageProcessor.java      |  3 ++-
 .../wrapper/xenbase/CitrixAttachIsoCommandWrapper.java     |  2 +-
 .../resource/wrapper/xenbase/XenServerUtilitiesHelper.java |  5 +++++
 systemvm/debian/etc/systemd/system/cloud-postinit.service  |  2 +-
 systemvm/debian/opt/cloud/bin/cs/CsApp.py                  |  2 +-
 systemvm/debian/opt/cloud/bin/cs/CsHelper.py               |  5 +++--
 9 files changed, 34 insertions(+), 11 deletions(-)

diff --cc plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java
index 75ee557,0000000..fd95da2
mode 100644,000000..100644
--- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java
+++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java
@@@ -1,698 -1,0 +1,698 @@@
 +// 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.discoverer;
 +
 +import java.net.InetAddress;
 +import java.net.URI;
 +import java.net.UnknownHostException;
 +import java.util.HashMap;
 +import java.util.LinkedList;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Queue;
 +import java.util.Set;
 +
 +import javax.inject.Inject;
 +import javax.naming.ConfigurationException;
 +import javax.persistence.EntityExistsException;
 +
 +import org.apache.cloudstack.hypervisor.xenserver.XenserverConfigs;
 +import org.apache.log4j.Logger;
 +import org.apache.xmlrpc.XmlRpcException;
 +
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.Listener;
 +import com.cloud.agent.api.AgentControlAnswer;
 +import com.cloud.agent.api.AgentControlCommand;
 +import com.cloud.agent.api.Answer;
 +import com.cloud.agent.api.Command;
 +import com.cloud.agent.api.SetupAnswer;
 +import com.cloud.agent.api.SetupCommand;
 +import com.cloud.agent.api.StartupCommand;
 +import com.cloud.agent.api.StartupRoutingCommand;
 +import com.cloud.alert.AlertManager;
 +import com.cloud.configuration.Config;
 +import com.cloud.dc.ClusterVO;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.HostPodVO;
 +import com.cloud.dc.dao.HostPodDao;
 +import com.cloud.exception.AgentUnavailableException;
 +import com.cloud.exception.ConnectionException;
 +import com.cloud.exception.DiscoveredWithErrorException;
 +import com.cloud.exception.DiscoveryException;
 +import com.cloud.exception.OperationTimedoutException;
 +import com.cloud.host.HostEnvironment;
 +import com.cloud.host.HostInfo;
 +import com.cloud.host.HostVO;
 +import com.cloud.host.Status;
 +import com.cloud.hypervisor.Hypervisor;
 +import com.cloud.hypervisor.Hypervisor.HypervisorType;
 +import com.cloud.hypervisor.xenserver.resource.CitrixHelper;
 +import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
 +import com.cloud.hypervisor.xenserver.resource.XcpOssResource;
 +import com.cloud.hypervisor.xenserver.resource.XcpServerResource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer56FP1Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer56Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer56SP2Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer600Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer620Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer620SP1Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServer650Resource;
 +import com.cloud.hypervisor.xenserver.resource.XenServerConnectionPool;
 +import com.cloud.hypervisor.xenserver.resource.Xenserver625Resource;
 +import com.cloud.resource.Discoverer;
 +import com.cloud.resource.DiscovererBase;
 +import com.cloud.resource.ResourceStateAdapter;
 +import com.cloud.resource.ServerResource;
 +import com.cloud.resource.UnableDeleteHostException;
 +import com.cloud.storage.Storage.ImageFormat;
 +import com.cloud.storage.Storage.TemplateType;
 +import com.cloud.storage.VMTemplateVO;
 +import com.cloud.storage.dao.VMTemplateDao;
 +import com.cloud.user.Account;
 +import com.cloud.utils.NumbersUtil;
 +import com.cloud.utils.db.QueryBuilder;
 +import com.cloud.utils.db.SearchCriteria.Op;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +import com.cloud.utils.exception.HypervisorVersionChangedException;
 +import com.xensource.xenapi.Connection;
 +import com.xensource.xenapi.Host;
 +import com.xensource.xenapi.HostPatch;
 +import com.xensource.xenapi.Pool;
 +import com.xensource.xenapi.PoolPatch;
 +import com.xensource.xenapi.Session;
 +import com.xensource.xenapi.Types.SessionAuthenticationFailed;
 +import com.xensource.xenapi.Types.UuidInvalid;
 +import com.xensource.xenapi.Types.XenAPIException;
 +
 +
 +public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, Listener, ResourceStateAdapter {
 +    private static final Logger s_logger = Logger.getLogger(XcpServerDiscoverer.class);
 +    private int _wait;
 +    private XenServerConnectionPool _connPool;
 +    private boolean _checkHvm;
 +    private boolean _setupMultipath;
 +    private String _instance;
 +
 +    @Inject
 +    private AlertManager _alertMgr;
 +    @Inject
 +    private AgentManager _agentMgr;
 +    @Inject
 +    private VMTemplateDao _tmpltDao;
 +    @Inject
 +    private HostPodDao _podDao;
 +
 +    private String xenServerIsoName = "xs-tools.iso";
 +    private String xenServerIsoDisplayText = "XenServer Tools Installer ISO (xen-pv-drv-iso)";
 +
 +    protected XcpServerDiscoverer() {
 +    }
 +
 +    void setClusterGuid(ClusterVO cluster, String guid) {
 +        cluster.setGuid(guid);
 +        try {
 +            _clusterDao.update(cluster.getId(), cluster);
 +        } catch (EntityExistsException e) {
 +            QueryBuilder<ClusterVO> sc = QueryBuilder.create(ClusterVO.class);
 +            sc.and(sc.entity().getGuid(), Op.EQ, guid);
 +            List<ClusterVO> clusters = sc.list();
 +            ClusterVO clu = clusters.get(0);
 +            List<HostVO> clusterHosts = _resourceMgr.listAllHostsInCluster(clu.getId());
 +            if (clusterHosts == null || clusterHosts.size() == 0) {
 +                clu.setGuid(null);
 +                _clusterDao.update(clu.getId(), clu);
 +                _clusterDao.update(cluster.getId(), cluster);
 +                return;
 +            }
 +            throw e;
 +        }
 +    }
 +
 +    protected boolean poolHasHotFix(Connection conn, String hostIp, String hotFixUuid) {
 +        try {
 +            Map<Host, Host.Record> hosts = Host.getAllRecords(conn);
 +            for (Map.Entry<Host, Host.Record> entry : hosts.entrySet()) {
 +
 +                Host.Record re = entry.getValue();
 +                if (!re.address.equalsIgnoreCase(hostIp)){
 +                    continue;
 +                }
 +                Set<HostPatch> patches = re.patches;
 +                PoolPatch poolPatch = PoolPatch.getByUuid(conn, hotFixUuid);
 +                for(HostPatch patch : patches) {
 +                    PoolPatch pp = patch.getPoolPatch(conn);
 +                    if (pp != null && pp.equals(poolPatch) && patch.getApplied(conn)) {
 +                        s_logger.debug("host " + hostIp + " does have " + hotFixUuid +" Hotfix.");
 +                        return true;
 +                    }
 +                }
 +            }
 +            return false;
 +        } catch (UuidInvalid e) {
 +            s_logger.debug("host " + hostIp + " doesn't have " + hotFixUuid + " Hotfix");
 +        } catch (Exception e) {
 +            s_logger.debug("can't get patches information, consider it doesn't have " + hotFixUuid + " Hotfix");
 +        }
 +        return false;
 +    }
 +
 +
 +
 +    @Override
 +    public Map<? extends ServerResource, Map<String, String>>
 +    find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List<String> hostTags) throws DiscoveryException {
 +        Map<CitrixResourceBase, Map<String, String>> resources = new HashMap<CitrixResourceBase, Map<String, String>>();
 +        Connection conn = null;
 +        if (!url.getScheme().equals("http")) {
 +            String msg = "urlString is not http so we're not taking care of the discovery for this: " + url;
 +            s_logger.debug(msg);
 +            return null;
 +        }
 +        if (clusterId == null) {
 +            String msg = "must specify cluster Id when add host";
 +            s_logger.debug(msg);
 +            throw new RuntimeException(msg);
 +        }
 +
 +        if (podId == null) {
 +            String msg = "must specify pod Id when add host";
 +            s_logger.debug(msg);
 +            throw new RuntimeException(msg);
 +        }
 +
 +        ClusterVO cluster = _clusterDao.findById(clusterId);
 +        if (cluster == null || cluster.getHypervisorType() != HypervisorType.XenServer) {
 +            if (s_logger.isInfoEnabled()) {
 +                s_logger.info("invalid cluster id or cluster is not for XenServer hypervisors");
 +            }
 +            return null;
 +        }
 +
 +        try {
 +            String hostname = url.getHost();
 +            InetAddress ia = InetAddress.getByName(hostname);
 +            String hostIp = ia.getHostAddress();
 +            Queue<String> pass = new LinkedList<String>();
 +            pass.add(password);
 +            conn = _connPool.getConnect(hostIp, username, pass);
 +            if (conn == null) {
 +                String msg = "Unable to get a connection to " + url;
 +                s_logger.debug(msg);
 +                throw new DiscoveryException(msg);
 +            }
 +
 +            Set<Pool> pools = Pool.getAll(conn);
 +            Pool pool = pools.iterator().next();
 +            Pool.Record pr = pool.getRecord(conn);
 +            String poolUuid = pr.uuid;
 +            Map<Host, Host.Record> hosts = Host.getAllRecords(conn);
 +            String latestHotFix = "";
 +            if (poolHasHotFix(conn, hostIp, XenserverConfigs.XSHotFix62ESP1004)) {
 +                latestHotFix = XenserverConfigs.XSHotFix62ESP1004;
 +            } else if (poolHasHotFix(conn, hostIp, XenserverConfigs.XSHotFix62ESP1)) {
 +                latestHotFix = XenserverConfigs.XSHotFix62ESP1;
 +            }
 +
 +            /*set cluster hypervisor type to xenserver*/
 +            ClusterVO clu = _clusterDao.findById(clusterId);
 +            if (clu.getGuid() == null) {
 +                setClusterGuid(clu, poolUuid);
 +            } else {
 +                List<HostVO> clusterHosts = _resourceMgr.listAllHostsInCluster(clusterId);
 +                if (clusterHosts != null && clusterHosts.size() > 0) {
 +                    if (!clu.getGuid().equals(poolUuid)) {
 +                        String msg = "Please join the host " +  hostIp + " to XS pool  "
 +                                + clu.getGuid() + " through XC/XS before adding it through CS UI";
 +                        s_logger.warn(msg);
 +                        throw new DiscoveryException(msg);
 +                    }
 +                } else {
 +                    setClusterGuid(clu, poolUuid);
 +                }
 +            }
 +            // can not use this conn after this point, because this host may join a pool, this conn is retired
 +            if (conn != null) {
 +                try {
 +                    Session.logout(conn);
 +                } catch (Exception e) {
 +                    s_logger.debug("Caught exception during logout", e);
 +                }
 +                conn.dispose();
 +                conn = null;
 +            }
 +
 +            poolUuid = clu.getGuid();
 +            _clusterDao.update(clusterId, clu);
 +
 +            if (_checkHvm) {
 +                for (Map.Entry<Host, Host.Record> entry : hosts.entrySet()) {
 +                    Host.Record record = entry.getValue();
 +
 +                    boolean support_hvm = false;
 +                    for (String capability : record.capabilities) {
 +                        if (capability.contains("hvm")) {
 +                            support_hvm = true;
 +                            break;
 +                        }
 +                    }
 +                    if (!support_hvm) {
 +                        String msg = "Unable to add host " + record.address + " because it doesn't support hvm";
 +                        _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, dcId, podId, msg, msg);
 +                        s_logger.debug(msg);
 +                        throw new RuntimeException(msg);
 +                    }
 +                }
 +            }
 +
 +            for (Map.Entry<Host, Host.Record> entry : hosts.entrySet()) {
 +                Host.Record record = entry.getValue();
 +                String hostAddr = record.address;
 +
 +                String prodVersion = CitrixHelper.getProductVersion(record);
 +                String xenVersion = record.softwareVersion.get("xen");
 +                String hostOS = record.softwareVersion.get("product_brand");
 +                if (hostOS == null) {
 +                    hostOS = record.softwareVersion.get("platform_name");
 +                }
 +
 +                String hostOSVer = prodVersion;
 +                String hostKernelVer = record.softwareVersion.get("linux");
 +
 +                if (_resourceMgr.findHostByGuid(record.uuid) != null) {
 +                    s_logger.debug("Skipping " + record.address + " because " + record.uuid + " is already in the database.");
 +                    continue;
 +                }
 +
 +                CitrixResourceBase resource = createServerResource(dcId, podId, record, latestHotFix);
 +                s_logger.info("Found host " + record.hostname + " ip=" + record.address + " product version=" + prodVersion);
 +
 +                Map<String, String> details = new HashMap<String, String>();
 +                Map<String, Object> params = new HashMap<String, Object>();
 +                details.put("url", hostAddr);
 +                details.put("username", username);
 +                params.put("username", username);
 +                details.put("password", password);
 +                params.put("password", password);
 +                params.put("zone", Long.toString(dcId));
 +                params.put("guid", record.uuid);
 +                params.put("pod", podId.toString());
 +                params.put("cluster", clusterId.toString());
 +                params.put("pool", poolUuid);
 +                params.put("ipaddress", record.address);
 +
 +                details.put(HostInfo.HOST_OS, hostOS);
 +                details.put(HostInfo.HOST_OS_VERSION, hostOSVer);
 +                details.put(HostInfo.HOST_OS_KERNEL_VERSION, hostKernelVer);
 +                details.put(HostInfo.HYPERVISOR_VERSION, xenVersion);
 +
 +                String privateNetworkLabel = _networkMgr.getDefaultManagementTrafficLabel(dcId, HypervisorType.XenServer);
 +                String storageNetworkLabel = _networkMgr.getDefaultStorageTrafficLabel(dcId, HypervisorType.XenServer);
 +
 +                if (!params.containsKey("private.network.device") && privateNetworkLabel != null) {
 +                    params.put("private.network.device", privateNetworkLabel);
 +                    details.put("private.network.device", privateNetworkLabel);
 +                }
 +
 +                if (!params.containsKey("storage.network.device1") && storageNetworkLabel != null) {
 +                    params.put("storage.network.device1", storageNetworkLabel);
 +                    details.put("storage.network.device1", storageNetworkLabel);
 +                }
 +
 +                DataCenterVO zone = _dcDao.findById(dcId);
 +                boolean securityGroupEnabled = zone.isSecurityGroupEnabled();
 +                params.put("securitygroupenabled", Boolean.toString(securityGroupEnabled));
 +
 +                params.put("router.aggregation.command.each.timeout", _configDao.getValue(Config.RouterAggregationCommandEachTimeout.toString()));
 +                params.put("wait", Integer.toString(_wait));
 +                details.put("wait", Integer.toString(_wait));
 +                params.put("migratewait", _configDao.getValue(Config.MigrateWait.toString()));
 +                params.put(Config.XenServerMaxNics.toString().toLowerCase(), _configDao.getValue(Config.XenServerMaxNics.toString()));
 +                params.put(Config.XenServerHeartBeatTimeout.toString().toLowerCase(), _configDao.getValue(Config.XenServerHeartBeatTimeout.toString()));
 +                params.put(Config.XenServerHeartBeatInterval.toString().toLowerCase(), _configDao.getValue(Config.XenServerHeartBeatInterval.toString()));
 +                params.put(Config.InstanceName.toString().toLowerCase(), _instance);
 +                details.put(Config.InstanceName.toString().toLowerCase(), _instance);
 +                try {
 +                    resource.configure("XenServer", params);
 +                } catch (ConfigurationException e) {
 +                    _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, dcId, podId, "Unable to add " + record.address, "Error is " + e.getMessage());
 +                    s_logger.warn("Unable to instantiate " + record.address, e);
 +                    continue;
 +                }
 +                resource.start();
 +                resources.put(resource, details);
 +            }
 +        } catch (SessionAuthenticationFailed e) {
 +            throw new DiscoveredWithErrorException("Authentication error");
 +        } catch (XenAPIException e) {
 +            s_logger.warn("XenAPI exception", e);
 +            return null;
 +        } catch (XmlRpcException e) {
 +            s_logger.warn("Xml Rpc Exception", e);
 +            return null;
 +        } catch (UnknownHostException e) {
 +            s_logger.warn("Unable to resolve the host name", e);
 +            return null;
 +        } catch (Exception e) {
 +            s_logger.debug("other exceptions: " + e.toString(), e);
 +            return null;
 +        }
 +        return resources;
 +    }
 +
 +    String getPoolUuid(Connection conn) throws XenAPIException, XmlRpcException {
 +        Map<Pool, Pool.Record> pools = Pool.getAllRecords(conn);
 +        assert pools.size() == 1 : "Pools size is " + pools.size();
 +        return pools.values().iterator().next().uuid;
 +    }
 +
 +    protected void addSamePool(Connection conn, Map<CitrixResourceBase, Map<String, String>> resources) throws XenAPIException, XmlRpcException {
 +        Map<Pool, Pool.Record> hps = Pool.getAllRecords(conn);
 +        assert (hps.size() == 1) : "How can it be more than one but it's actually " + hps.size();
 +
 +        // This is the pool.
 +        String poolUuid = hps.values().iterator().next().uuid;
 +
 +        for (Map<String, String> details : resources.values()) {
 +            details.put("pool", poolUuid);
 +        }
 +    }
 +
 +    protected CitrixResourceBase createServerResource(String prodBrand, String prodVersion, String prodVersionTextShort, String hotfix) {
 +        // Xen Cloud Platform group of hypervisors
 +        if (prodBrand.equals("XCP") && (prodVersion.equals("1.0.0") || prodVersion.equals("1.1.0")
 +                || prodVersion.equals("5.6.100") || prodVersion.startsWith("1.4") || prodVersion.startsWith("1.6"))) {
 +            return new XcpServerResource();
 +        } // Citrix Xenserver group of hypervisors
 +        else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.0")) {
 +            return new XenServer56Resource();
 +        } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.0.0")) {
 +            return new XenServer600Resource();
 +        } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.0.2")) {
 +            return new XenServer600Resource();
 +        } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.1.0")) {
 +            return new XenServer610Resource();
 +        } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.2.0")) {
 +            if (hotfix != null && hotfix.equals(XenserverConfigs.XSHotFix62ESP1004)) {
 +                return new Xenserver625Resource();
 +            } else if (hotfix != null && hotfix.equals(XenserverConfigs.XSHotFix62ESP1)) {
 +                return new XenServer620SP1Resource();
 +            } else {
 +                return new XenServer620Resource();
 +            }
 +        } else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.100")) {
 +            if ("5.6 SP2".equals(prodVersionTextShort.trim())) {
 +                return new XenServer56SP2Resource();
 +            } else if ("5.6 FP1".equals(prodVersionTextShort.trim())) {
 +                return new XenServer56FP1Resource();
 +            }
 +        } else if (prodBrand.equals("XCP_Kronos")) {
 +            return new XcpOssResource();
-         } else if (prodBrand.equals("XenServer")) {
++        } else if (prodBrand.equals("XenServer") || prodBrand.equals("XCP-ng")) {
 +            final String[] items = prodVersion.split("\\.");
 +            if ((Integer.parseInt(items[0]) > 6) ||
 +                    (Integer.parseInt(items[0]) == 6 && Integer.parseInt(items[1]) >= 4)) {
 +                s_logger.warn("defaulting to xenserver650 resource for product brand: " + prodBrand + " with product " +
 +                        "version: " + prodVersion);
 +                //default to xenserver650 resource.
 +                return new XenServer650Resource();
 +            }
 +        }
 +        String msg =
 +                "Only support XCP 1.0.0, 1.1.0, 1.4.x, 1.5 beta, 1.6.x; XenServer 5.6,  XenServer 5.6 FP1, XenServer 5.6 SP2, Xenserver 6.0, 6.0.2, 6.1.0, 6.2.0, >6.4.0 but this one is " +
 +                        prodBrand + " " + prodVersion;
 +        s_logger.warn(msg);
 +        throw new RuntimeException(msg);
 +    }
 +
 +
 +
 +    protected CitrixResourceBase createServerResource(long dcId, Long podId, Host.Record record, String hotfix) {
 +        String prodBrand = record.softwareVersion.get("product_brand");
 +        if (prodBrand == null) {
 +            prodBrand = record.softwareVersion.get("platform_name").trim();
 +        } else {
 +            prodBrand = prodBrand.trim();
 +        }
 +        String prodVersion = CitrixHelper.getProductVersion(record);
 +
 +        String prodVersionTextShort = record.softwareVersion.get("product_version_text_short");
 +        return createServerResource(prodBrand, prodVersion, prodVersionTextShort, hotfix);
 +    }
 +
 +    protected void serverConfig() {
 +        String value = _params.get(Config.XenServerSetupMultipath.key());
 +        _setupMultipath = Boolean.parseBoolean(value);
 +    }
 +
 +    @Override
 +    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
 +        super.configure(name, params);
 +        serverConfig();
 +
 +        String value = _params.get(Config.XapiWait.toString());
 +        _wait = NumbersUtil.parseInt(value, Integer.parseInt(Config.XapiWait.getDefaultValue()));
 +
 +        _instance = _params.get(Config.InstanceName.key());
 +
 +        value = _params.get("xenserver.check.hvm");
 +        _checkHvm = Boolean.parseBoolean(value);
 +        _connPool = XenServerConnectionPool.getInstance();
 +
 +        _agentMgr.registerForHostEvents(this, true, false, true);
 +
 +        createXenServerToolsIsoEntryInDatabase();
 +        _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this);
 +        return true;
 +    }
 +
 +    @Override
 +    public boolean matchHypervisor(String hypervisor) {
 +        if (hypervisor == null) {
 +            return true;
 +        }
 +        return Hypervisor.HypervisorType.XenServer.toString().equalsIgnoreCase(hypervisor);
 +    }
 +
 +    @Override
 +    public Hypervisor.HypervisorType getHypervisorType() {
 +        return Hypervisor.HypervisorType.XenServer;
 +    }
 +
 +    @Override
 +    public void postDiscovery(List<HostVO> hosts, long msId) throws DiscoveryException {
 +        //do nothing
 +    }
 +
 +    @Override
 +    public int getTimeout() {
 +        return 0;
 +    }
 +
 +    @Override
 +    public boolean isRecurring() {
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean processAnswers(long agentId, long seq, Answer[] answers) {
 +        return false;
 +    }
 +
 +    @Override
 +    public boolean processCommands(long agentId, long seq, Command[] commands) {
 +        return false;
 +    }
 +
 +    /**
 +     * Create the XenServer tools ISO entry in the database.
 +     * If there is already an entry with 'isoName' equals to {@value #xenServerIsoName} , we update its 'displayText' to {@value #xenServerIsoDisplayText}.
 +     * Otherwise, we create a new entry.
 +     */
 +    protected void createXenServerToolsIsoEntryInDatabase() {
 +        VMTemplateVO tmplt = _tmpltDao.findByTemplateName(xenServerIsoName);
 +        if (tmplt == null) {
 +            long id = _tmpltDao.getNextInSequence(Long.class, "id");
 +            VMTemplateVO template = VMTemplateVO.createPreHostIso(id, xenServerIsoName, xenServerIsoName, ImageFormat.ISO, true, true, TemplateType.PERHOST, null, null, true, 64,
 +                    Account.ACCOUNT_ID_SYSTEM, null, xenServerIsoDisplayText, false, 1, false, HypervisorType.XenServer);
 +            _tmpltDao.persist(template);
 +        } else {
 +            long id = tmplt.getId();
 +            tmplt.setTemplateType(TemplateType.PERHOST);
 +            tmplt.setUrl(null);
 +            tmplt.setDisplayText(xenServerIsoDisplayText);
 +            _tmpltDao.update(id, tmplt);
 +        }
 +    }
 +
 +    @Override
 +    public void processHostAdded(long hostId) {
 +    }
 +
 +    @Override
 +    public void processConnect(com.cloud.host.Host agent, StartupCommand cmd, boolean forRebalance) throws ConnectionException {
 +        if (!(cmd instanceof StartupRoutingCommand)) {
 +            return;
 +        }
 +        long agentId = agent.getId();
 +
 +        StartupRoutingCommand startup = (StartupRoutingCommand)cmd;
 +        if (startup.getHypervisorType() != HypervisorType.XenServer) {
 +            s_logger.debug("Not XenServer so moving on.");
 +            return;
 +        }
 +
 +        HostVO host = _hostDao.findById(agentId);
 +
 +        ClusterVO cluster = _clusterDao.findById(host.getClusterId());
 +        if (cluster.getGuid() == null) {
 +            cluster.setGuid(startup.getPool());
 +            _clusterDao.update(cluster.getId(), cluster);
 +        } else if (!cluster.getGuid().equals(startup.getPool())) {
 +            String msg = "pool uuid for cluster " + cluster.getId() + " changed from " + cluster.getGuid() + " to " + startup.getPool();
 +            s_logger.warn(msg);
 +            throw new CloudRuntimeException(msg);
 +        }
 +
 +        Map<String, String> details = startup.getHostDetails();
 +        String prodBrand = details.get("product_brand").trim();
 +        String prodVersion = details.get("product_version").trim();
 +        String hotfix = details.get(XenserverConfigs.XS620HotFix);
 +        String prodVersionTextShort = details.get("product_version_text_short");
 +
 +        String resource = createServerResource(prodBrand, prodVersion, prodVersionTextShort, hotfix).getClass().getName();
 +
 +        if (!resource.equals(host.getResource())) {
 +            String msg = "host " + host.getPrivateIpAddress() + " changed from " + host.getResource() + " to " + resource;
 +            s_logger.debug(msg);
 +            host.setResource(resource);
 +            host.setSetup(false);
 +            _hostDao.update(agentId, host);
 +            throw new HypervisorVersionChangedException(msg);
 +        }
 +
 +        if (s_logger.isDebugEnabled()) {
 +            s_logger.debug("Setting up host " + agentId);
 +        }
 +        HostEnvironment env = new HostEnvironment();
 +
 +        SetupCommand setup = new SetupCommand(env);
 +        if (_setupMultipath) {
 +            setup.setMultipathOn();
 +        }
 +        if (!host.isSetup()) {
 +            setup.setNeedSetup(true);
 +        }
 +
 +        try {
 +            Answer answer = _agentMgr.send(agentId, setup);
 +            if (answer != null && answer.getResult() && answer instanceof SetupAnswer) {
 +                host.setSetup(true);
 +                host.setLastPinged((System.currentTimeMillis() >> 10) - 5 * 60);
 +                host.setHypervisorVersion(prodVersion);
 +                _hostDao.update(host.getId(), host);
 +                if (((SetupAnswer)answer).needReconnect()) {
 +                    throw new ConnectionException(false, "Reinitialize agent after setup.");
 +                }
 +                return;
 +            } else {
 +                s_logger.warn("Unable to setup agent " + agentId + " due to " + ((answer != null) ? answer.getDetails() : "return null"));
 +            }
 +        } catch (AgentUnavailableException e) {
 +            s_logger.warn("Unable to setup agent " + agentId + " because it became unavailable.", e);
 +        } catch (OperationTimedoutException e) {
 +            s_logger.warn("Unable to setup agent " + agentId + " because it timed out", e);
 +        }
 +        throw new ConnectionException(true, "Reinitialize agent after setup.");
 +    }
 +
 +    @Override
 +    public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) {
 +        return null;
 +    }
 +
 +    @Override
 +    public boolean processDisconnect(long agentId, Status state) {
 +        return false;
 +    }
 +
 +    @Override
 +    public void processHostAboutToBeRemoved(long hostId) {
 +    }
 +
 +    @Override
 +    public void processHostRemoved(long hostId, long clusterId) {
 +    }
 +
 +    @Override
 +    public boolean processTimeout(long agentId, long seq) {
 +        return false;
 +    }
 +
 +    @Override
 +    public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +    @Override
 +    public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) {
 +        StartupCommand firstCmd = startup[0];
 +        if (!(firstCmd instanceof StartupRoutingCommand)) {
 +            return null;
 +        }
 +
 +        StartupRoutingCommand ssCmd = ((StartupRoutingCommand)firstCmd);
 +        if (ssCmd.getHypervisorType() != HypervisorType.XenServer) {
 +            return null;
 +        }
 +
 +        HostPodVO pod = _podDao.findById(host.getPodId());
 +        DataCenterVO dc = _dcDao.findById(host.getDataCenterId());
 +        s_logger.info("Host: " + host.getName() + " connected with hypervisor type: " + HypervisorType.XenServer + ". Checking CIDR...");
 +        _resourceMgr.checkCIDR(pod, dc, ssCmd.getPrivateIpAddress(), ssCmd.getPrivateNetmask());
 +        return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.XenServer, details, hostTags);
 +    }
 +
 +    @Override
 +    public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
 +        if (host.getType() != com.cloud.host.Host.Type.Routing || host.getHypervisorType() != HypervisorType.XenServer) {
 +            return null;
 +        }
 +
 +        _resourceMgr.deleteRoutingHost(host, isForced, isForceDeleteStorage);
 +        return new DeleteHostAnswer(true);
 +    }
 +
 +    @Override
 +    protected HashMap<String, Object> buildConfigParams(HostVO host) {
 +        HashMap<String, Object> params = super.buildConfigParams(host);
 +        DataCenterVO zone = _dcDao.findById(host.getDataCenterId());
 +        if (zone != null) {
 +            boolean securityGroupEnabled = zone.isSecurityGroupEnabled();
 +            params.put("securitygroupenabled", Boolean.toString(securityGroupEnabled));
 +        }
 +        return params;
 +    }
 +
 +    @Override
 +    public boolean stop() {
 +        _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName());
 +        return super.stop();
 +    }
 +}
diff --cc plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
index 7124659,0000000..e1066b7
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,5610 -1,0 +1,5614 @@@
 +// 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.CollectionUtils;
 +import org.apache.commons.collections.MapUtils;
 +import org.apache.commons.io.FileUtils;
 +import org.apache.commons.lang3.BooleanUtils;
 +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, 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;
 +
 +    private 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 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
 +            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");
++            Set<SR> srs = SR.getByNameLabel(conn, "XenServer Tools");
 +            if (srs.size() != 1) {
-                 throw new CloudRuntimeException("There are " + srs.size() + " SRs with name XenServer Tools");
++                s_logger.debug("Failed to find SR by name 'XenServer Tools', will try to find 'XCP-ng Tools' SR");
++                srs = SR.getByNameLabel(conn, "XCP-ng 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(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);
 +        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(" ", "%");
 +            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) {
 +        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);
 +                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) {
++        if ((xenBrand.equals("XenServer") || xenBrand.equals("XCP-ng")) && 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;
 +    }
 +
 +    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");
 +        }
 +        List<StartupStorageCommand> startUpLocalStorageCommands = null;
 +        try {
 +            startUpLocalStorageCommands = initializeLocalSrs(conn);
 +        } catch (XenAPIException | XmlRpcException e) {
 +            s_logger.warn("Could not initialize local SRs on host: " + _host.getUuid(), e);
 +        }
 +        if (CollectionUtils.isEmpty(startUpLocalStorageCommands)) {
 +            return new StartupCommand[] {cmd};
 +        }
 +        return createStartupCommandsArray(cmd, startUpLocalStorageCommands);
 +    }
 +
 +    /**
 +     * We simply create an array and add the {@link StartupRoutingCommand} as the first element of the array. Then, we add all elements from startUpLocalStorageCommands
 +     */
 +    private StartupCommand[] createStartupCommandsArray(StartupRoutingCommand startupRoutingCommand, List<StartupStorageCommand> startUpLocalStorageCommands) {
 +        StartupCommand[] startupCommands = new StartupCommand[startUpLocalStorageCommands.size() + 1];
 +        startupCommands[0] = startupRoutingCommand;
 +        for (int i = 1; i < startupCommands.length; i++) {
 +            startupCommands[i] = startUpLocalStorageCommands.get(i - 1);
 +        }
 +        return startupCommands;
 +    }
 +
 +    /**
 +     * This  method will return a list of all local SRs.
 +     * An SR is considered local if it meets all of the following criteria:
 +     * <ul>
 +     *  <li> {@link Record#shared} is equal to false
 +     *  <li> The PBDs of the SR ({@link Record#PBDs}) are connected to host {@link #_host}
 +     *  <li> SR type is equal to the {@link SRType} sent as parameter
 +     * </ul>
 +     */
 +    protected List<SR> getAllLocalSrForType(Connection conn, SRType srType) throws XenAPIException, XmlRpcException {
 +        List<SR> localSrs = new ArrayList<>();
 +        Map<SR, SR.Record> allSrRecords = SR.getAllRecords(conn);
 +        if (MapUtils.isEmpty(allSrRecords)) {
 +            return localSrs;
 +        }
 +        for (Map.Entry<SR, SR.Record> entry : allSrRecords.entrySet()) {
 +            SR.Record srRec = entry.getValue();
 +            if (!srType.equals(srRec.type)) {
 +                continue;
 +            }
 +            if (BooleanUtils.toBoolean(srRec.shared)) {
 +                continue;
 +            }
 +            Set<PBD> pbds = srRec.PBDs;
 +            if (CollectionUtils.isEmpty(pbds)) {
 +                continue;
 +            }
 +            for (PBD pbd : pbds) {
 +                Host host = pbd.getHost(conn);
 +                if (!isRefNull(host) && org.apache.commons.lang3.StringUtils.equals(host.getUuid(conn), _host.getUuid())) {
 +                    if (!pbd.getCurrentlyAttached(conn)) {
 +                        s_logger.debug(String.format("PBD [%s] of local SR [%s] was unplugged, pluggin it now", pbd.getUuid(conn), srRec.uuid));
 +                        pbd.plug(conn);
 +                    }
 +                    s_logger.debug("Scanning local SR: " + srRec.uuid);
 +                    SR sr = entry.getKey();
 +                    sr.scan(conn);
 +                    localSrs.add(sr);
 +                }
 +            }
 +        }
 +        s_logger.debug(String.format("Found %d local storage of type [%s] for host [%s]", localSrs.size(), srType.toString(), _host.getUuid()));
 +        return localSrs;
 +    }
 +
 +    /**
 +     *  This method will prepare Local SRs to be used by Apache CloudStack.
 +     */
 +    protected List<StartupStorageCommand> initializeLocalSrs(Connection conn) throws XenAPIException, XmlRpcException {
 +        List<StartupStorageCommand> localStorageStartupCommands = new ArrayList<>();
 +        List<SR> allLocalSrs = getAllLocalSrs(conn);
 +
 +        for (SR sr : allLocalSrs) {
 +            long totalCapacity = sr.getPhysicalSize(conn);
 +            if (totalCapacity > 0) {
 +                StartupStorageCommand cmd = createStartUpStorageCommand(conn, sr);
 +                localStorageStartupCommands.add(cmd);
 +            }
 +        }
 +        return localStorageStartupCommands;
 +    }
 +
 +    /**
 +     * This method will retrieve all Local SRs according to {@link #getAllLocalSrForType(Connection, SRType)}.
 +     * The types used are {@link SRType#LVM} and {@link SRType#EXT}.
 +     *
 +     */
 +    protected List<SR> getAllLocalSrs(Connection conn) throws XenAPIException, XmlRpcException {
 +        List<SR> allLocalSrLvmType = getAllLocalSrForType(conn, SRType.LVM);
 +        List<SR> allLocalSrExtType = getAllLocalSrForType(conn, SRType.EXT);
 +        List<SR> allLocalSrs = new ArrayList<>(allLocalSrLvmType);
 +        allLocalSrs.addAll(allLocalSrExtType);
 +        return allLocalSrs;
 +    }
 +
 +    /**
 +     * This method creates the StartUp storage command for the local SR.
 +     * We will configure 'name-label' and 'description' using {@link #configureStorageNameAndDescription(Connection, SR)}.
 +     * Then, we will create the POJO {@link StoragePoolInfo} with SR's information using method {@link #createStoragePoolInfo(Connection, SR)}.
 +     */
 +    protected StartupStorageCommand createStartUpStorageCommand(Connection conn, SR sr) throws XenAPIException, XmlRpcException {
 +        configureStorageNameAndDescription(conn, sr);
 +
 +        StoragePoolInfo storagePoolInfo = createStoragePoolInfo(conn, sr);
 +
 +        StartupStorageCommand cmd = new StartupStorageCommand();
 +        cmd.setPoolInfo(storagePoolInfo);
 +        cmd.setGuid(_host.getUuid());
 +        cmd.setDataCenter(Long.toString(_dcId));
 +        cmd.setResourceType(Storage.StorageResourceType.STORAGE_POOL);
 +
 +        String.format("StartUp command created for local storage [%s] of type [%s] on host [%s]", storagePoolInfo.getUuid(), storagePoolInfo.getPoolType(), _host.getUuid());
 +        return cmd;
 +    }
 +
 +    /**
 +     *  Instantiate {@link StoragePoolInfo} with SR's information.
 +     */
 +    protected StoragePoolInfo createStoragePoolInfo(Connection conn, SR sr) throws XenAPIException, XmlRpcException {
 +        long totalCapacity = sr.getPhysicalSize(conn);
 +        String srUuid = sr.getUuid(conn);
 +        Host host = Host.getByUuid(conn, _host.getUuid());
 +        String address = host.getAddress(conn);
 +        long availableCapacity = totalCapacity - sr.getPhysicalUtilisation(conn);
 +        String srType = sr.getType(conn).toUpperCase();
 +        return new StoragePoolInfo(srUuid, address, srType, srType, StoragePoolType.valueOf(srType), totalCapacity, availableCapacity);
 +    }
 +
 +    protected void configureStorageNameAndDescription(Connection conn, SR sr) throws XenAPIException, XmlRpcException {
 +        String srUuid = sr.getUuid(conn);
 +        sr.setNameLabel(conn, srUuid);
 +
 +        String nameFormat = "Cloud Stack Local (%s) Storage Pool for %s";
 +        sr.setNameDescription(conn, String.format(nameFormat, sr.getType(conn), _host.getUuid()));
 +    }
 +
 +    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", e);
 +                    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", e);
 +                    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.