You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by de...@apache.org on 2013/10/02 11:20:22 UTC

[02/31] A plugin for Hyper-V control is available for CloudStack. The plugin implements basic VM control; however, its architecture allows additional functionality to be easily added. Incorporating the plugin in CloudStack will allow the community to

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/conf/log4j-cloud.xml.in
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/conf/log4j-cloud.xml.in b/plugins/hypervisors/hyperv/conf/log4j-cloud.xml.in
new file mode 100644
index 0000000..fdbba19
--- /dev/null
+++ b/plugins/hypervisors/hyperv/conf/log4j-cloud.xml.in
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+   <!-- ================================= -->
+   <!-- Preserve messages in a local file -->
+   <!-- ================================= -->
+
+   <!-- A time/date based rolling appender -->
+   <appender name="FILE" class="org.apache.log4j.rolling.RollingFileAppender">
+      <param name="Append" value="true"/>
+      <param name="Threshold" value="INFO"/>
+      <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
+        <param name="FileNamePattern" value="@AGENTLOG@.%d{yyyy-MM-dd}.gz"/>
+        <param name="ActiveFileName" value="@AGENTLOG@"/>
+      </rollingPolicy>
+      <layout class="org.apache.log4j.EnhancedPatternLayout">
+         <param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{3}] (%t:%x) %m%n"/>
+      </layout>
+   </appender>
+   
+   <!-- ============================== -->
+   <!-- Append messages to the console -->
+   <!-- ============================== -->
+
+   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+      <param name="Target" value="System.out"/>
+      <param name="Threshold" value="INFO"/>
+
+      <layout class="org.apache.log4j.PatternLayout">
+         <param name="ConversionPattern" value="%d{ISO8601}{GMT} %-5p [%c{3}] (%t:%x) %m%n"/>
+      </layout>
+   </appender>
+
+   <!-- ================ -->
+   <!-- Limit categories -->
+   <!-- ================ -->
+
+   <category name="com.cloud">
+     <priority value="INFO"/>
+   </category>
+   
+   <category name="com.cloud.agent.metrics">
+     <priority value="INFO"/>
+   </category>
+   
+   <category name="com.cloud.agent.resource.computing.ComputingResource$StorageMonitorTask">
+     <priority value="INFO"/>
+   </category>
+
+   <!-- Limit the org.apache category to INFO as its DEBUG is verbose -->
+   <category name="org.apache">
+      <priority value="INFO"/>
+   </category>
+
+   <category name="org">
+      <priority value="INFO"/>
+   </category>
+   
+   <category name="net">
+     <priority value="INFO"/>
+   </category>
+
+   <!-- ======================= -->
+   <!-- Setup the Root category -->
+   <!-- ======================= -->
+
+   <root>
+      <level value="INFO"/>
+      <appender-ref ref="CONSOLE"/>
+      <appender-ref ref="FILE"/>
+   </root>
+
+</log4j:configuration>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/conf/log4j.xml
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/conf/log4j.xml b/plugins/hypervisors/hyperv/conf/log4j.xml
new file mode 100644
index 0000000..1e97ce9
--- /dev/null
+++ b/plugins/hypervisors/hyperv/conf/log4j.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+   <!-- ================================= -->
+   <!-- Preserve messages in a local file -->
+   <!-- ================================= -->
+
+   <!-- A time/date based rolling appender -->
+   <appender name="FILE" class="org.apache.log4j.rolling.RollingFileAppender">
+      <param name="Append" value="true"/>
+      <param name="Threshold" value="TRACE"/>
+      <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
+        <param name="FileNamePattern" value="log/@AGENTLOG@.%d{yyyy-MM-dd}.gz"/>
+        <param name="ActiveFileName" value="log/@AGENTLOG@"/>
+      </rollingPolicy>
+      <layout class="org.apache.log4j.EnhancedPatternLayout">
+         <param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{3}] (%t:%x) %m%n"/>
+      </layout>
+   </appender>
+   
+   <!-- ============================== -->
+   <!-- Append messages to the console -->
+   <!-- ============================== -->
+
+   <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+      <param name="Target" value="System.out"/>
+      <param name="Threshold" value="TRACE"/>
+
+      <layout class="org.apache.log4j.PatternLayout">
+         <param name="ConversionPattern" value="%d{ISO8601}{GMT} %-5p [%c{3}] (%t:%x) %m%n"/>
+      </layout>
+   </appender>
+
+   <!-- ================ -->
+   <!-- Limit categories -->
+   <!-- ================ -->
+
+   <category name="com.cloud">
+     <priority value="TRACE"/>
+   </category>
+   
+   <category name="com.cloud.agent.metrics">
+     <priority value="TRACE"/>
+   </category>
+   
+   <category name="com.cloud.agent.resource.computing.ComputingResource$StorageMonitorTask">
+     <priority value="TRACE"/>
+   </category>
+
+   <!-- Limit the org.apache category to TRACE as its DEBUG is verbose -->
+   <category name="org.apache">
+      <priority value="TRACE"/>
+   </category>
+
+   <category name="org">
+      <priority value="TRACE"/>
+   </category>
+   
+   <category name="net">
+     <priority value="TRACE"/>
+   </category>
+
+   <!-- ======================= -->
+   <!-- Setup the Root category -->
+   <!-- ======================= -->
+
+   <root>
+      <level value="TRACE"/>
+      <appender-ref ref="CONSOLE"/>
+      <appender-ref ref="FILE"/>
+   </root>
+
+</log4j:configuration>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/pom.xml b/plugins/hypervisors/hyperv/pom.xml
new file mode 100644
index 0000000..e9f0371
--- /dev/null
+++ b/plugins/hypervisors/hyperv/pom.xml
@@ -0,0 +1,158 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-plugin-hypervisor-hyperv</artifactId>
+  <name>Apache CloudStack Plugin - Hypervisor Hyper-V</name>
+  <parent>
+    <groupId>org.apache.cloudstack</groupId>
+    <artifactId>cloudstack-plugins</artifactId>
+    <version>4.3.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+  <properties>
+    <skipTests>true</skipTests>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-agent</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.mortbay.jetty</groupId>
+      <artifactId>jetty</artifactId>
+      <version>6.1.26</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-utils</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <defaultGoal>install</defaultGoal>
+    <sourceDirectory>src</sourceDirectory>
+    <testSourceDirectory>test</testSourceDirectory>
+    <resources>
+      <resource>
+        <directory>conf</directory>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.2.1</version>
+        <configuration>
+          <executable>java</executable>
+          <mainClass>com.cloud.agent.AgentShell</mainClass>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.12</version>
+        <configuration>
+          <!-- Exclude tests that require an agent, they appear under a different profile -->
+            <excludes>
+              <exclude>**/HypervDirectConnectResourceTest.*</exclude>
+            </excludes>
+            <includes>
+              <include>none</include>
+            </includes>
+            <skipTests>${skipTests}</skipTests>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <profiles>
+    <!-- Hyper-V plugin is built using mono -->
+    <profile>
+      <id>hyperv-agent</id>
+      <activation>
+        <property>
+          <name>hyperv-agent</name>
+        </property>
+      </activation>
+<!--      <dependencies>
+          <dependency>
+              <groupId>org.apache.cloudstack</groupId>
+              <artifactId>cloud-plugin-hypervisor-hyperv</artifactId>
+              <version>${project.version}</version>
+          </dependency>
+      </dependencies> --> 
+      <build>
+      <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.2.1</version>
+        <executions>
+          <execution>
+            <phase>compile</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <executable>bash</executable>
+          <arguments>
+            <argument>./buildagent.sh</argument>
+          </arguments>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.12</version>
+        <configuration>
+            <excludes>
+              <exclude>none</exclude>
+            </excludes>            
+            <includes>
+              <include>**/HypervDirectConnectResourceTest.java</include>
+            </includes>
+            <skipTests>${skipTests}</skipTests>            
+        </configuration>
+      </plugin>
+      </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/scripts/dev_extra_setup.sh
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/scripts/dev_extra_setup.sh b/plugins/hypervisors/hyperv/scripts/dev_extra_setup.sh
new file mode 100644
index 0000000..aa77c4b
--- /dev/null
+++ b/plugins/hypervisors/hyperv/scripts/dev_extra_setup.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+mvn -P developer -pl developer -Ddeploydb
+cp client/target/cloud-client-ui-4.2.0-SNAPSHOT/WEB-INF/classes/log4j{-cloud,}.xml
+mysql --user=root --password="" cloud -e "update configuration set value='false' where name='developer';"
+mysql --user=root --password="" cloud -e "INSERT INTO configuration (instance, name,value) VALUE('DEFAULT','system.vm.use.local.storage', 'true');" 
+update template_view set url='http://10.70.176.29/pub/systemvmtemplate-2013-07-04-master-hyperv.vhd' where  name='SystemVM Template (HyperV)';
+export MAVEN_OPTS="-XX:MaxPermSize=256m -Xmx1g"
+mvn -pl :cloud-client-ui jetty:run

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java
new file mode 100644
index 0000000..11df222
--- /dev/null
+++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/discoverer/HypervServerDiscoverer.java
@@ -0,0 +1,467 @@
+// 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.hyperv.discoverer;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.Listener;
+import com.cloud.agent.api.AgentControlAnswer;
+import com.cloud.agent.api.AgentControlCommand;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.ReadyCommand;
+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.dc.ClusterDetailsDao;
+import com.cloud.dc.ClusterVO;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.HostPodVO;
+import com.cloud.dc.dao.ClusterDao;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.HostPodDao;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.ConnectionException;
+import com.cloud.exception.DiscoveryException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.Host;
+import com.cloud.host.HostEnvironment;
+import com.cloud.host.HostVO;
+import com.cloud.host.Status;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.hypervisor.hyperv.resource.HypervDirectConnectResource;
+import com.cloud.resource.Discoverer;
+import com.cloud.resource.DiscovererBase;
+import com.cloud.resource.ResourceManager;
+import com.cloud.resource.ResourceStateAdapter;
+import com.cloud.resource.ServerResource;
+import com.cloud.resource.UnableDeleteHostException;
+
+/**
+ * Methods to discover and managem a Hyper-V agent. Prepares a
+ * HypervDirectConnectResource corresponding to the agent on a Hyper-V
+ * hypervisor and manages its lifecycle.
+ */
+@Local(value = Discoverer.class)
+public class HypervServerDiscoverer extends DiscovererBase implements
+        Discoverer, Listener, ResourceStateAdapter {
+    private static final Logger s_logger = Logger
+            .getLogger(HypervServerDiscoverer.class);
+
+    @Inject
+    private HostDao _hostDao = null;
+    @Inject
+    private ClusterDao _clusterDao;
+    @Inject
+    private ClusterDetailsDao _clusterDetailsDao;
+    @Inject
+    private ResourceManager _resourceMgr;
+    @Inject
+    private HostPodDao _podDao;
+    @Inject
+    private DataCenterDao _dcDao;
+
+    // TODO: AgentManager and AlertManager not being used to transmit info,
+    // may want to reconsider.
+    @Inject
+    private AgentManager _agentMgr;
+    @Inject
+    private AlertManager _alertMgr;
+
+    // Listener interface methods
+
+    @Override
+    public final boolean processAnswers(final long agentId, final long seq,
+            final Answer[] answers) {
+        return false;
+    }
+
+    @Override
+    public final boolean processCommands(final long agentId, final long seq,
+            final Command[] commands) {
+        return false;
+    }
+
+    @Override
+    public final AgentControlAnswer processControlCommand(final long agentId,
+            final AgentControlCommand cmd) {
+        return null;
+    }
+
+    @Override
+    public final void processConnect(final Host agent,
+            final StartupCommand cmd, final boolean forRebalance)
+            throws ConnectionException {
+        // Limit the commands we can process
+        if (!(cmd instanceof StartupRoutingCommand)) {
+            return;
+        }
+
+        StartupRoutingCommand startup = (StartupRoutingCommand) cmd;
+
+        // assert
+        if (startup.getHypervisorType() != HypervisorType.Hyperv) {
+            s_logger.debug("Not Hyper-V hypervisor, so moving on.");
+            return;
+        }
+
+        long agentId = agent.getId();
+        HostVO host = _hostDao.findById(agentId);
+
+        // Our Hyper-V machines are not participating in pools, and the pool id
+        // we provide them is not persisted.
+        // This means the pool id can vary.
+        ClusterVO cluster = _clusterDao.findById(host.getClusterId());
+        if (cluster.getGuid() == null) {
+            cluster.setGuid(startup.getPool());
+            _clusterDao.update(cluster.getId(), cluster);
+        }
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Setting up host " + agentId);
+        }
+
+        HostEnvironment env = new HostEnvironment();
+        SetupCommand setup = new SetupCommand(env);
+        if (!host.isSetup()) {
+            setup.setNeedSetup(true);
+        }
+
+        try {
+            SetupAnswer answer = (SetupAnswer) _agentMgr.send(agentId, setup);
+            if (answer != null && answer.getResult()) {
+                host.setSetup(true);
+                // TODO: clean up magic numbers below
+                host.setLastPinged((System.currentTimeMillis() >> 10) - 5 * 60);
+                _hostDao.update(host.getId(), host);
+                if (answer.needReconnect()) {
+                    throw new ConnectionException(false,
+                            "Reinitialize agent after setup.");
+                }
+                return;
+            } else {
+                String reason = answer.getDetails();
+                if (reason == null) {
+                    reason = " details were null";
+                }
+                s_logger.warn("Unable to setup agent " + agentId + " due to "
+                        + reason);
+            }
+            // Error handling borrowed from XcpServerDiscoverer, may need to be
+            // updated.
+        } 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 final boolean processDisconnect(final long agentId,
+            final Status state) {
+        return false;
+    }
+
+    @Override
+    public final boolean isRecurring() {
+        return false;
+    }
+
+    @Override
+    public final int getTimeout() {
+        return 0;
+    }
+
+    @Override
+    public final boolean processTimeout(final long agentId, final long seq) {
+        return false;
+    }
+
+    // End Listener implementation
+
+    // Returns server component used by server manager to operate the plugin.
+    // Server component is a ServerResource. If a connected agent is used, the
+    // ServerResource is
+    // ignored in favour of another created in response to
+    @Override
+    public final Map<? extends ServerResource, Map<String, String>> find(
+            final long dcId, final Long podId, final Long clusterId,
+            final URI uri, final String username, final String password,
+            final List<String> hostTags) throws DiscoveryException {
+
+        if (s_logger.isInfoEnabled()) {
+            s_logger.info("Discover host. dc(zone): " + dcId + ", pod: "
+                    + podId + ", cluster: " + clusterId + ", uri host: "
+                    + uri.getHost());
+        }
+
+        // Assertions
+        if (podId == null) {
+            if (s_logger.isInfoEnabled()) {
+                s_logger.info("No pod is assigned, skipping the discovery in"
+                        + " Hyperv discoverer");
+            }
+            return null;
+        }
+        ClusterVO cluster = _clusterDao.findById(clusterId); // ClusterVO exists
+        // in the
+        // database
+        if (cluster == null) {
+            if (s_logger.isInfoEnabled()) {
+                s_logger.info("No cluster in database for cluster id "
+                        + clusterId);
+            }
+            return null;
+        }
+        if (cluster.getHypervisorType() != HypervisorType.Hyperv) {
+            if (s_logger.isInfoEnabled()) {
+                s_logger.info("Cluster " + clusterId
+                        + "is not for Hyperv hypervisors");
+            }
+            return null;
+        }
+        if (!uri.getScheme().equals("http")) {
+            String msg = "urlString is not http so we're not taking care of"
+                    + " the discovery for this: " + uri;
+            s_logger.debug(msg);
+            return null;
+        }
+
+        try {
+            String hostname = uri.getHost();
+            InetAddress ia = InetAddress.getByName(hostname);
+            String agentIp = ia.getHostAddress();
+            String uuidSeed = agentIp;
+            String guidWithTail = calcServerResourceGuid(uuidSeed)
+                    + "-HypervResource";
+
+            if (_resourceMgr.findHostByGuid(guidWithTail) != null) {
+                s_logger.debug("Skipping " + agentIp + " because "
+                        + guidWithTail + " is already in the database.");
+                return null;
+            }
+
+            s_logger.info("Creating"
+                    + HypervDirectConnectResource.class.getName()
+                    + " HypervDummyResourceBase for zone/pod/cluster " + dcId
+                    + "/" + podId + "/" + clusterId);
+
+            // Some Hypervisors organise themselves in pools.
+            // The startup command tells us what pool they are using.
+            // In the meantime, we have to place a GUID corresponding to the
+            // pool in the database
+            // This GUID may change.
+            if (cluster.getGuid() == null) {
+                cluster.setGuid(UUID.nameUUIDFromBytes(
+                        String.valueOf(clusterId).getBytes()).toString());
+                _clusterDao.update(clusterId, cluster);
+            }
+
+            // Settings required by all server resources managing a hypervisor
+            Map<String, Object> params = new HashMap<String, Object>();
+            params.put("zone", Long.toString(dcId));
+            params.put("pod", Long.toString(podId));
+            params.put("cluster", Long.toString(clusterId));
+            params.put("guid", guidWithTail);
+            params.put("ipaddress", agentIp);
+
+            // Hyper-V specific settings
+            Map<String, String> details = new HashMap<String, String>();
+            details.put("url", uri.getHost());
+            details.put("username", username);
+            details.put("password", password);
+            details.put("cluster.guid", cluster.getGuid());
+
+            params.putAll(details);
+
+            HypervDirectConnectResource resource =
+                    new HypervDirectConnectResource();
+            resource.configure(agentIp, params);
+
+            // Assert
+            // TODO: test by using bogus URL and bogus virtual path in URL
+            ReadyCommand ping = new ReadyCommand();
+            Answer pingAns = resource.executeRequest(ping);
+            if (pingAns == null || !pingAns.getResult()) {
+                String errMsg =
+                        "Agent not running, or no route to agent on at "
+                                + uri;
+                s_logger.debug(errMsg);
+                throw new DiscoveryException(errMsg);
+            }
+
+            Map<HypervDirectConnectResource, Map<String, String>> resources =
+                    new HashMap<HypervDirectConnectResource,
+                    Map<String, String>>();
+            resources.put(resource, details);
+
+            // TODO: does the resource have to create a connection?
+            return resources;
+        } catch (ConfigurationException e) {
+            _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, dcId, podId,
+                    "Unable to add " + uri.getHost(),
+                    "Error is " + e.getMessage());
+            s_logger.warn("Unable to instantiate " + uri.getHost(), e);
+        } catch (UnknownHostException e) {
+            _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, dcId, podId,
+                    "Unable to add " + uri.getHost(),
+                    "Error is " + e.getMessage());
+            s_logger.warn("Unable to instantiate " + uri.getHost(), e);
+        } catch (Exception e) {
+            String msg = " can't setup agent, due to " + e.toString() + " - "
+                    + e.getMessage();
+            s_logger.warn(msg);
+        }
+        return null;
+    }
+
+    /**
+     * Encapsulate GUID calculation in public method to allow access to test
+     * programs. Works by converting a string to a GUID using
+     * UUID.nameUUIDFromBytes
+     *
+     * @param uuidSeed
+     *            string to use to generate GUID
+     *
+     * @return GUID in form of a string.
+     */
+    public static String calcServerResourceGuid(final String uuidSeed) {
+        String guid = UUID.nameUUIDFromBytes(uuidSeed.getBytes()).toString();
+        return guid;
+    }
+
+    // Adapter implementation: (facilitates plug in loading)
+    // Required because Discoverer extends Adapter
+    // Overrides Adapter.configure to always return true
+    // Inherit Adapter.getName
+    // Inherit Adapter.stop
+    // Inherit Adapter.start
+    @Override
+    public final boolean configure(final String name,
+            final Map<String, Object> params) throws ConfigurationException {
+        super.configure(name, params);
+
+        // TODO: allow timeout on we HTTPRequests to be configured
+        _agentMgr.registerForHostEvents(this, true, false, true);
+        _resourceMgr.registerResourceStateAdapter(this.getClass()
+                .getSimpleName(), this);
+        return true;
+    }
+
+    // end of Adapter
+
+    @Override
+    public void postDiscovery(final List<HostVO> hosts, final long msId)
+            throws DiscoveryException {
+    }
+
+    @Override
+    public final Hypervisor.HypervisorType getHypervisorType() {
+        return Hypervisor.HypervisorType.Hyperv;
+    }
+
+    // TODO: verify that it is okay to return true on null hypervisor
+    @Override
+    public final boolean matchHypervisor(final String hypervisor) {
+        if (hypervisor == null) {
+            return true;
+        }
+        return Hypervisor.HypervisorType.Hyperv.toString().equalsIgnoreCase(
+                hypervisor);
+    }
+
+    // end of Discoverer
+
+    // ResourceStateAdapter
+    @Override
+    public final HostVO createHostVOForConnectedAgent(final HostVO host,
+            final StartupCommand[] cmd) {
+        return null;
+    }
+
+    // TODO: add test for method
+    @Override
+    public final HostVO createHostVOForDirectConnectAgent(final HostVO host,
+            final StartupCommand[] startup, final ServerResource resource,
+            final Map<String, String> details, final List<String> hostTags) {
+        StartupCommand firstCmd = startup[0];
+        if (!(firstCmd instanceof StartupRoutingCommand)) {
+            return null;
+        }
+
+        StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd);
+        if (ssCmd.getHypervisorType() != HypervisorType.Hyperv) {
+            return null;
+        }
+
+        s_logger.info("Host: " + host.getName()
+                + " connected with hypervisor type: " + HypervisorType.Hyperv
+                + ". Checking CIDR...");
+
+        HostPodVO pod = _podDao.findById(host.getPodId());
+        DataCenterVO dc = _dcDao.findById(host.getDataCenterId());
+
+        _resourceMgr.checkCIDR(pod, dc, ssCmd.getPrivateIpAddress(),
+                ssCmd.getPrivateNetmask());
+
+        return _resourceMgr.fillRoutingHostVO(host, ssCmd,
+                HypervisorType.Hyperv, details, hostTags);
+    }
+
+    // TODO: add test for method
+    @Override
+    public final DeleteHostAnswer deleteHost(final HostVO host,
+            final boolean isForced, final boolean isForceDeleteStorage)
+            throws UnableDeleteHostException {
+        // assert
+        if (host.getType() != Host.Type.Routing
+                || host.getHypervisorType() != HypervisorType.Hyperv) {
+            return null;
+        }
+        _resourceMgr.deleteRoutingHost(host, isForced, isForceDeleteStorage);
+        return new DeleteHostAnswer(true);
+    }
+
+    @Override
+    public final boolean stop() {
+        _resourceMgr.unregisterResourceStateAdapter(this.getClass()
+                .getSimpleName());
+        return super.stop();
+    }
+    // end of ResourceStateAdapter
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java
new file mode 100644
index 0000000..8b79cae
--- /dev/null
+++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java
@@ -0,0 +1,68 @@
+// 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.hyperv.guru;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.hypervisor.HypervisorGuru;
+import com.cloud.hypervisor.HypervisorGuruBase;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineProfile;
+
+/**
+ * Implementation of Hypervisor guru for Hyper-V.
+ **/
+@Local(value = HypervisorGuru.class)
+public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru {
+
+    @Inject
+    private GuestOSDao _guestOsDao;
+
+    @Override
+    public final HypervisorType getHypervisorType() {
+        return HypervisorType.Hyperv;
+    }
+    /**
+     * Prevent direct creation.
+     */
+    protected HypervGuru() {
+        super();
+    }
+
+    @Override
+    public final VirtualMachineTO implement(
+            VirtualMachineProfile vm) {
+        VirtualMachineTO to = toVirtualMachineTO(vm);
+
+        // Determine the VM's OS description
+        GuestOSVO guestOS = _guestOsDao.findById(vm.getVirtualMachine()
+                .getGuestOSId());
+        to.setOs(guestOS.getDisplayName());
+
+        return to;
+    }
+
+    @Override
+    public final boolean trackVmHostChange() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/1d0a931d/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java
new file mode 100644
index 0000000..e22f284
--- /dev/null
+++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java
@@ -0,0 +1,454 @@
+// 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.hyperv.resource;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.ConfigurationException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.PingCommand;
+import com.cloud.agent.api.PingRoutingCommand;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.agent.api.StartupRoutingCommand;
+import com.cloud.agent.api.StartupStorageCommand;
+import com.cloud.agent.api.UnsupportedAnswer;
+import com.cloud.agent.api.StartupRoutingCommand.VmState;
+import com.cloud.host.Host.Type;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.network.Networks.RouterPrivateIpStrategy;
+import com.cloud.resource.ServerResource;
+import com.cloud.resource.ServerResourceBase;
+import com.cloud.serializer.GsonHelper;
+import com.google.gson.Gson;
+
+/**
+ * Implementation of dummy resource to be returned from discoverer.
+ **/
+
+public class HypervDirectConnectResource extends ServerResourceBase implements
+        ServerResource {
+    public static final int DEFAULT_AGENT_PORT = 8250;
+    private static final Logger s_logger = Logger
+            .getLogger(HypervDirectConnectResource.class.getName());
+
+    private static final Gson s_gson = GsonHelper.getGson();
+    private String _zoneId;
+    private String _podId;
+    private String _clusterId;
+    private String _guid;
+    private String _agentIp;
+    private int _port = DEFAULT_AGENT_PORT;
+
+    private String _clusterGuid;
+
+    // Used by initialize to assert object configured before
+    // initialize called.
+    private boolean _configureCalled = false;
+
+    private String _username;
+    private String _password;
+
+    @Override
+    public final Type getType() {
+        return Type.Routing;
+    }
+
+    @Override
+    public final StartupCommand[] initialize() {
+        // assert
+        if (!_configureCalled) {
+            String errMsg =
+                    this.getClass().getName()
+                            + " requires configure() be called before"
+                            + " initialize()";
+            s_logger.error(errMsg);
+        }
+
+        // Create default StartupRoutingCommand, then customise
+        StartupRoutingCommand defaultStartRoutCmd =
+                new StartupRoutingCommand(0, 0, 0, 0, null,
+                        Hypervisor.HypervisorType.Hyperv,
+                        RouterPrivateIpStrategy.HostLocal,
+                        new HashMap<String, VmState>());
+
+        // Identity within the data centre is decided by CloudStack kernel,
+        // and passed via ServerResource.configure()
+        defaultStartRoutCmd.setDataCenter(_zoneId);
+        defaultStartRoutCmd.setPod(_podId);
+        defaultStartRoutCmd.setCluster(_clusterId);
+        defaultStartRoutCmd.setGuid(_guid);
+        defaultStartRoutCmd.setName(_name);
+        defaultStartRoutCmd.setPrivateIpAddress(_agentIp);
+        defaultStartRoutCmd.setStorageIpAddress(_agentIp);
+        defaultStartRoutCmd.setPool(_clusterGuid);
+
+        s_logger.debug("Generated StartupRoutingCommand for _agentIp \""
+                + _agentIp + "\"");
+
+        // TODO: does version need to be hard coded.
+        defaultStartRoutCmd.setVersion("4.2.0");
+
+        // Specifics of the host's resource capacity and network configuration
+        // comes from the host itself. CloudStack sanity checks network
+        // configuration
+        // and uses capacity info for resource allocation.
+        Command[] startCmds =
+                requestStartupCommand(new Command[] {defaultStartRoutCmd});
+
+        // TODO: may throw, is this okay?
+        StartupRoutingCommand startCmd = (StartupRoutingCommand) startCmds[0];
+
+        // Assert that host identity is consistent with existing values.
+        if (startCmd == null) {
+            String errMsg =
+                    String.format("Host %s (IP %s)"
+                            + "did not return a StartupRoutingCommand",
+                            _name, _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+        if (!startCmd.getDataCenter().equals(
+                defaultStartRoutCmd.getDataCenter())) {
+            String errMsg =
+                    String.format(
+                            "Host %s (IP %s) changed zone/data center.  Was "
+                                    + defaultStartRoutCmd.getDataCenter()
+                                    + " NOW its " + startCmd.getDataCenter(),
+                            _name, _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+        if (!startCmd.getPod().equals(defaultStartRoutCmd.getPod())) {
+            String errMsg =
+                    String.format("Host %s (IP %s) changed pod.  Was "
+                            + defaultStartRoutCmd.getPod() + " NOW its "
+                            + startCmd.getPod(), _name, _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+        if (!startCmd.getCluster().equals(defaultStartRoutCmd.getCluster())) {
+            String errMsg =
+                    String.format("Host %s (IP %s) changed cluster.  Was "
+                            + defaultStartRoutCmd.getCluster() + " NOW its "
+                            + startCmd.getCluster(), _name, _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+        if (!startCmd.getGuid().equals(defaultStartRoutCmd.getGuid())) {
+            String errMsg =
+                    String.format("Host %s (IP %s) changed guid.  Was "
+                            + defaultStartRoutCmd.getGuid() + " NOW its "
+                            + startCmd.getGuid(), _name, _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+        if (!startCmd.getPrivateIpAddress().equals(
+                defaultStartRoutCmd.getPrivateIpAddress())) {
+            String errMsg =
+                    String.format("Host %s (IP %s) IP address.  Was "
+                            + defaultStartRoutCmd.getPrivateIpAddress()
+                            + " NOW its " + startCmd.getPrivateIpAddress(),
+                            _name, _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+        if (!startCmd.getName().equals(defaultStartRoutCmd.getName())) {
+            String errMsg =
+                    String.format(
+                            "Host %s (IP %s) name.  Was " + startCmd.getName()
+                                    + " NOW its "
+                                    + defaultStartRoutCmd.getName(), _name,
+                            _agentIp);
+            s_logger.error(errMsg);
+            // TODO: valid to return null, or should we throw?
+            return null;
+        }
+
+        // Host will also supply details of an existing StoragePool if it has
+        // been configured with one.
+        //
+        // NB: if the host was configured
+        // with a local storage pool, CloudStack may not be able to use it
+        // unless
+        // it is has service offerings configured to recognise this storage
+        // type.
+        StartupStorageCommand storePoolCmd = null;
+        if (startCmds.length > 1) {
+            storePoolCmd = (StartupStorageCommand) startCmds[1];
+            // TODO: is this assertion required?
+            if (storePoolCmd == null) {
+                String frmtStr =
+                        "Host %s (IP %s) sent incorrect Command, "
+                                + "second parameter should be a "
+                                + "StartupStorageCommand";
+                String errMsg = String.format(frmtStr, _name, _agentIp);
+                s_logger.error(errMsg);
+                // TODO: valid to return null, or should we throw?
+                return null;
+            }
+            s_logger.info("Host " + _name + " (IP " + _agentIp
+                    + ") already configured with a storeage pool, details "
+                    + s_gson.toJson(startCmds[1]));
+        } else {
+            s_logger.info("Host " + _name + " (IP " + _agentIp
+                    + ") already configured with a storeage pool, details ");
+        }
+        return new StartupCommand[] {startCmd, storePoolCmd};
+    }
+
+    @Override
+    public final PingCommand getCurrentStatus(final long id) {
+        PingCommand pingCmd = new PingRoutingCommand(getType(), id, null);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Ping host " + _name + " (IP " + _agentIp + ")");
+        }
+
+        Answer pingAns = this.executeRequest(pingCmd);
+
+        if (pingAns == null || !pingAns.getResult()) {
+            s_logger.info("Cannot ping host " + _name + " (IP " + _agentIp
+                    + "), pingAns (blank means null) is:" + pingAns);
+            return null;
+        }
+        return pingCmd;
+    }
+
+    // TODO: Is it valid to return NULL, or should we throw on error?
+    // Returns StartupCommand with fields revised with values known only to the
+    // host
+    public final Command[] requestStartupCommand(final Command[] cmd) {
+        // Set HTTP POST destination URI
+        // Using java.net.URI, see
+        // http://docs.oracle.com/javase/1.5.0/docs/api/java/net/URI.html
+        URI agentUri = null;
+        try {
+            String cmdName = StartupCommand.class.getName();
+            agentUri =
+                    new URI("http", null, _agentIp, _port,
+                            "/api/HypervResource/" + cmdName, null, null);
+        } catch (URISyntaxException e) {
+            // TODO add proper logging
+            String errMsg = "Could not generate URI for Hyper-V agent";
+            s_logger.error(errMsg, e);
+            return null;
+        }
+        String incomingCmd = postHttpRequest(s_gson.toJson(cmd), agentUri);
+
+        if (incomingCmd == null) {
+            return null;
+        }
+        Command[] result = null;
+        try {
+            result = s_gson.fromJson(incomingCmd, Command[].class);
+        } catch (Exception ex) {
+            String errMsg = "Failed to deserialize Command[] " + incomingCmd;
+            s_logger.error(errMsg, ex);
+        }
+        s_logger.debug("requestStartupCommand received response "
+                + s_gson.toJson(result));
+        if (result.length > 0) {
+            return result;
+        }
+        return null;
+    }
+
+    // TODO: Is it valid to return NULL, or should we throw on error?
+    @Override
+    public final Answer executeRequest(final Command cmd) {
+        // Set HTTP POST destination URI
+        // Using java.net.URI, see
+        // http://docs.oracle.com/javase/1.5.0/docs/api/java/net/URI.html
+        URI agentUri = null;
+        try {
+            String cmdName = cmd.getClass().getName();
+            agentUri =
+                    new URI("http", null, _agentIp, _port,
+                            "/api/HypervResource/" + cmdName, null, null);
+        } catch (URISyntaxException e) {
+            // TODO add proper logging
+            String errMsg = "Could not generate URI for Hyper-V agent";
+            s_logger.error(errMsg, e);
+            return null;
+        }
+        String ansStr = postHttpRequest(s_gson.toJson(cmd), agentUri);
+
+        if (ansStr == null) {
+            return null;
+        }
+        // Only Answer instances are returned by remote agents.
+        // E.g. see Response.getAnswers()
+        Answer[] result = s_gson.fromJson(ansStr, Answer[].class);
+        s_logger.debug("executeRequest received response "
+                + s_gson.toJson(result));
+        if (result.length > 0) {
+            return result[0];
+        }
+        return null;
+    }
+
+    public static String postHttpRequest(final String jsonCmd,
+            final URI agentUri) {
+        // Using Apache's HttpClient for HTTP POST
+        // Java-only approach discussed at on StackOverflow concludes with
+        // comment to use Apache HttpClient
+        // http://stackoverflow.com/a/2793153/939250, but final comment is to
+        // use Apache.
+        s_logger.debug("POST request to" + agentUri.toString()
+                + " with contents" + jsonCmd);
+
+        // Create request
+        HttpClient httpClient = new DefaultHttpClient();
+        String result = null;
+
+        // TODO: are there timeout settings and worker thread settings to tweak?
+        try {
+            HttpPost request = new HttpPost(agentUri);
+
+            // JSON encode command
+            // Assumes command sits comfortably in a string, i.e. not used for
+            // large data transfers
+            StringEntity cmdJson = new StringEntity(jsonCmd);
+            request.addHeader("content-type", "application/json");
+            request.setEntity(cmdJson);
+            s_logger.debug("Sending cmd to " + agentUri.toString()
+                    + " cmd data:" + jsonCmd);
+            HttpResponse response = httpClient.execute(request);
+
+            // Unsupported commands will not route.
+            if (response.getStatusLine().getStatusCode()
+                == HttpStatus.SC_NOT_FOUND) {
+                String errMsg =
+                        "Failed to send : HTTP error code : "
+                                + response.getStatusLine().getStatusCode();
+                s_logger.error(errMsg);
+                String unsupportMsg =
+                        "Unsupported command "
+                                + agentUri.getPath()
+                                + ".  Are you sure you got the right type of"
+                                + " server?";
+                Answer ans = new UnsupportedAnswer(null, unsupportMsg);
+                s_logger.error(ans);
+                result = s_gson.toJson(new Answer[] {ans});
+            } else if (response.getStatusLine().getStatusCode()
+                != HttpStatus.SC_OK) {
+                String errMsg =
+                        "Failed send to " + agentUri.toString()
+                                + " : HTTP error code : "
+                                + response.getStatusLine().getStatusCode();
+                s_logger.error(errMsg);
+                return null;
+            } else {
+                result = EntityUtils.toString(response.getEntity());
+                s_logger.debug("POST response is" + result);
+            }
+        } catch (ClientProtocolException protocolEx) {
+            // Problem with HTTP message exchange
+            s_logger.error(protocolEx);
+        } catch (IOException connEx) {
+            // Problem with underlying communications
+            s_logger.error(connEx);
+        } finally {
+            httpClient.getConnectionManager().shutdown();
+        }
+        return result;
+    }
+
+    @Override
+    protected final String getDefaultScriptsDir() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    // NB: 'params' can come from one of two places.
+    // For a new host, HypervServerDiscoverer.find().
+    // For an existing host, DiscovererBase.reloadResource().
+    // In the later case, the params Map is populated with predefined keys
+    // and custom keys from the database that were passed out by the find()
+    // call.
+    // the custom keys go by the variable name 'details'.
+    // Thus, in find(), you see that 'details' are added to the params Map.
+    @Override
+    public final boolean configure(final String name,
+            final Map<String, Object> params) throws ConfigurationException {
+        /* todo: update, make consistent with the xen server equivalent. */
+        _guid = (String) params.get("guid");
+        _zoneId = (String) params.get("zone");
+        _podId = (String) params.get("pod");
+        _clusterId = (String) params.get("cluster");
+        _agentIp = (String) params.get("ipaddress"); // was agentIp
+        _name = name;
+
+        _clusterGuid = (String) params.get("cluster.guid");
+        _username = (String) params.get("url");
+        _password = (String) params.get("password");
+        _username = (String) params.get("username");
+
+        _configureCalled = true;
+        return true;
+    }
+
+    @Override
+    public void setName(final String name) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void setConfigParams(final Map<String, Object> params) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public final Map<String, Object> getConfigParams() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public final int getRunLevel() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void setRunLevel(final int level) {
+        // TODO Auto-generated method stub
+    }
+
+}