diff --git a/agent/pom.xml b/agent/pom.xml
index 14133226053..c3ac308882a 100644
--- a/agent/pom.xml
+++ b/agent/pom.xml
@@ -115,5 +115,38 @@
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+
+
+ maven-antrun-plugin
+
+ [1.7,)
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/agent/src/com/cloud/agent/dao/impl/PropertiesStorage.java b/agent/src/com/cloud/agent/dao/impl/PropertiesStorage.java
index 411d946a294..0c100b37be6 100755
--- a/agent/src/com/cloud/agent/dao/impl/PropertiesStorage.java
+++ b/agent/src/com/cloud/agent/dao/impl/PropertiesStorage.java
@@ -119,34 +119,33 @@ public class PropertiesStorage implements StorageComponent {
return true;
}
- @Override
- public void setName(String name) {
- // TODO Auto-generated method stub
-
- }
+ @Override
+ public void setName(String name) {
+ // TODO Auto-generated method stub
- @Override
- public void setConfigParams(Map params) {
- // TODO Auto-generated method stub
-
- }
+ }
- @Override
- public Map getConfigParams() {
- // TODO Auto-generated method stub
- return null;
- }
+ @Override
+ public void setConfigParams(Map params) {
+ // TODO Auto-generated method stub
- @Override
- public int getRunLevel() {
- // TODO Auto-generated method stub
- return 0;
- }
+ }
- @Override
- public void setRunLevel(int level) {
- // TODO Auto-generated method stub
-
- }
+ @Override
+ public Map getConfigParams() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getRunLevel() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void setRunLevel(int level) {
+ // TODO Auto-generated method stub
+ }
}
diff --git a/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java b/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java
index 73a994e8bd9..432caa63c32 100644
--- a/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java
+++ b/agent/src/com/cloud/agent/dhcp/FakeDhcpSnooper.java
@@ -18,9 +18,6 @@ package com.cloud.agent.dhcp;
import java.net.InetAddress;
import java.net.UnknownHostException;
-
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
@@ -32,8 +29,6 @@ import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.Command;
import com.cloud.utils.Pair;
import com.cloud.utils.net.NetUtils;
diff --git a/agent/src/com/cloud/agent/mockvm/MockVm.java b/agent/src/com/cloud/agent/mockvm/MockVm.java
index 306a6c40786..8a806eea354 100644
--- a/agent/src/com/cloud/agent/mockvm/MockVm.java
+++ b/agent/src/com/cloud/agent/mockvm/MockVm.java
@@ -71,11 +71,4 @@ public class MockVm {
return vncPort;
}
- public static void main(String[] args) {
- long i = 10;
- Long l = null;
- if (i == l) {
- System.out.print("fdfd");
- }
- }
}
diff --git a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
index 6f49f47a1ed..054acd1caeb 100644
--- a/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
+++ b/agent/src/com/cloud/agent/resource/consoleproxy/ConsoleProxyResource.java
@@ -32,9 +32,12 @@ import java.util.Properties;
import javax.naming.ConfigurationException;
-import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.log4j.Logger;
+import com.google.gson.Gson;
+
+import org.apache.cloudstack.managed.context.ManagedContextRunnable;
+
import com.cloud.agent.Agent.ExitStatus;
import com.cloud.agent.api.AgentControlAnswer;
import com.cloud.agent.api.Answer;
@@ -61,7 +64,6 @@ import com.cloud.resource.ServerResourceBase;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.script.Script;
-import com.google.gson.Gson;
/**
*
@@ -130,19 +132,6 @@ public class ConsoleProxyResource extends ServerResourceBase implements
}
}
- private boolean copyCertToDirectory(String certificate, String filePath)
- throws IOException {
- boolean success;
- // copy cert to the dir
- FileWriter fstream = new FileWriter(filePath);
- BufferedWriter out = new BufferedWriter(fstream);
- out.write(certificate);
- // Close the output stream
- out.close();
- success = true;
- return success;
- }
-
protected Answer execute(final CheckConsoleProxyLoadCommand cmd) {
return executeProxyLoadScan(cmd, cmd.getProxyVmId(),
cmd.getProxyVmName(), cmd.getProxyManagementIp(),
@@ -204,6 +193,7 @@ public class ConsoleProxyResource extends ServerResourceBase implements
return null;
}
+ @Override
public Type getType() {
return Host.Type.ConsoleProxy;
}
diff --git a/api/src/com/cloud/agent/api/to/DataStoreTO.java b/api/src/com/cloud/agent/api/to/DataStoreTO.java
index b79ba7d64be..199740da471 100644
--- a/api/src/com/cloud/agent/api/to/DataStoreTO.java
+++ b/api/src/com/cloud/agent/api/to/DataStoreTO.java
@@ -21,6 +21,7 @@ package com.cloud.agent.api.to;
import com.cloud.storage.DataStoreRole;
public interface DataStoreTO {
- public DataStoreRole getRole();
- public String getUuid();
+ DataStoreRole getRole();
+ String getUuid();
+ String getUrl();
}
diff --git a/api/src/com/cloud/agent/api/to/NfsTO.java b/api/src/com/cloud/agent/api/to/NfsTO.java
index 54683c7f410..ee6c3e3566d 100644
--- a/api/src/com/cloud/agent/api/to/NfsTO.java
+++ b/api/src/com/cloud/agent/api/to/NfsTO.java
@@ -39,6 +39,7 @@ public class NfsTO implements DataStoreTO {
}
+ @Override
public String getUrl() {
return _url;
}
diff --git a/api/src/com/cloud/agent/api/to/S3TO.java b/api/src/com/cloud/agent/api/to/S3TO.java
index 350b9ca8b60..0dabb8ff933 100644
--- a/api/src/com/cloud/agent/api/to/S3TO.java
+++ b/api/src/com/cloud/agent/api/to/S3TO.java
@@ -181,6 +181,11 @@ public final class S3TO implements S3Utils.ClientOptions, DataStoreTO {
return this.uuid;
}
+ @Override
+ public String getUrl() {
+ return null;
+ }
+
public void setUuid(final String uuid) {
this.uuid = uuid;
}
diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java
index 49f380b3d6d..bda3326cb51 100644
--- a/api/src/com/cloud/network/Network.java
+++ b/api/src/com/cloud/network/Network.java
@@ -114,6 +114,7 @@ public interface Network extends ControlledEntity, StateObject, I
private static List supportedProviders = new ArrayList();
public static final Provider VirtualRouter = new Provider("VirtualRouter", false);
+ public static final Provider JuniperContrail = new Provider("JuniperContrail", false);
public static final Provider JuniperSRX = new Provider("JuniperSRX", true);
public static final Provider F5BigIp = new Provider("F5BigIp", true);
public static final Provider Netscaler = new Provider("Netscaler", true);
diff --git a/api/src/com/cloud/network/Site2SiteVpnConnection.java b/api/src/com/cloud/network/Site2SiteVpnConnection.java
index 810f9992d93..984b0519ddc 100644
--- a/api/src/com/cloud/network/Site2SiteVpnConnection.java
+++ b/api/src/com/cloud/network/Site2SiteVpnConnection.java
@@ -35,4 +35,5 @@ public interface Site2SiteVpnConnection extends ControlledEntity, InternalIdenti
public State getState();
public Date getCreated();
public Date getRemoved();
+ public boolean isPassive();
}
diff --git a/api/src/com/cloud/vm/snapshot/VMSnapshotService.java b/api/src/com/cloud/vm/snapshot/VMSnapshotService.java
index 83f86bc90db..1ae7edc7a8e 100644
--- a/api/src/com/cloud/vm/snapshot/VMSnapshotService.java
+++ b/api/src/com/cloud/vm/snapshot/VMSnapshotService.java
@@ -35,7 +35,7 @@ public interface VMSnapshotService {
VMSnapshot getVMSnapshotById(Long id);
- VMSnapshot creatVMSnapshot(Long vmId, Long vmSnapshotId);
+ VMSnapshot creatVMSnapshot(Long vmId, Long vmSnapshotId, Boolean quiescevm);
VMSnapshot allocVMSnapshot(Long vmId, String vsDisplayName, String vsDescription, Boolean snapshotMemory)
throws ResourceAllocationException;
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 32c2c5e9637..d8879109e87 100755
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -490,6 +490,7 @@ public class ApiConstants {
public static final String VM_SNAPSHOT_ID = "vmsnapshotid";
public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids";
public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory";
+ public static final String VM_SNAPSHOT_QUIESCEVM = "quiescevm";
public static final String IMAGE_STORE_UUID = "imagestoreuuid";
public static final String GUEST_VM_CIDR = "guestvmcidr";
public static final String NETWORK_CIDR = "networkcidr";
@@ -536,6 +537,7 @@ public class ApiConstants {
public static final String RESOURCE_DETAILS = "resourcedetails";
public static final String EXPUNGE = "expunge";
public static final String FOR_DISPLAY = "fordisplay";
+ public static final String PASSIVE = "passive";
public enum HostDetails {
diff --git a/api/src/org/apache/cloudstack/api/Parameter.java b/api/src/org/apache/cloudstack/api/Parameter.java
index 6f64737ab83..89178f200e7 100644
--- a/api/src/org/apache/cloudstack/api/Parameter.java
+++ b/api/src/org/apache/cloudstack/api/Parameter.java
@@ -22,6 +22,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.BaseCmd.CommandType;
@Retention(RetentionPolicy.RUNTIME)
@@ -48,4 +49,6 @@ public @interface Parameter {
String since() default "";
String retrieveMethod() default "getById";
+
+ RoleType[] authorized() default {};
}
diff --git a/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java
index f4960dd0e08..59220eefd4f 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/usage/GetUsageRecordsCmd.java
@@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.usage;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import java.util.TimeZone;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
@@ -33,6 +32,8 @@ import org.apache.cloudstack.api.response.UsageRecordResponse;
import org.apache.cloudstack.usage.Usage;
import org.apache.log4j.Logger;
+import com.cloud.utils.Pair;
+
@APICommand(name = "listUsageRecords", description="Lists usage records for accounts", responseObject=UsageRecordResponse.class)
public class GetUsageRecordsCmd extends BaseListCmd {
public static final Logger s_logger = Logger.getLogger(GetUsageRecordsCmd.class.getName());
@@ -110,16 +111,18 @@ public class GetUsageRecordsCmd extends BaseListCmd {
@Override
public void execute(){
- List extends Usage> usageRecords = _usageService.getUsageRecords(this);
+ Pair, Integer> usageRecords = _usageService.getUsageRecords(this);
ListResponse response = new ListResponse();
List usageResponses = new ArrayList();
- for(Usage usageRecord: usageRecords){
- UsageRecordResponse usageResponse = _responseGenerator.createUsageResponse(usageRecord);
- usageResponse.setObjectName("usagerecord");
- usageResponses.add(usageResponse);
+ if (usageRecords != null) {
+ for(Usage usageRecord: usageRecords.first()){
+ UsageRecordResponse usageResponse = _responseGenerator.createUsageResponse(usageRecord);
+ usageResponse.setObjectName("usagerecord");
+ usageResponses.add(usageResponse);
+ }
+ response.setResponses(usageResponses, usageRecords.second());
}
-
- response.setResponses(usageResponses);
+
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java
index 542215a5e8e..252a206d000 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/CreateVMSnapshotCmd.java
@@ -53,6 +53,9 @@ public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd {
@Parameter(name = ApiConstants.VM_SNAPSHOT_MEMORY, type = CommandType.BOOLEAN, required = false, description = "snapshot memory if true")
private Boolean snapshotMemory;
+ @Parameter(name = ApiConstants.VM_SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "quiesce vm if true")
+ private Boolean quiescevm;
+
public Boolean snapshotMemory() {
if (snapshotMemory == null) {
return false;
@@ -61,6 +64,14 @@ public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd {
}
}
+ public Boolean getQuiescevm() {
+ if (quiescevm == null) {
+ return false;
+ } else {
+ return quiescevm;
+ }
+ }
+
public String getDisplayName() {
return displayName;
}
@@ -97,7 +108,7 @@ public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd {
@Override
public void execute() {
CallContext.current().setEventDetails("VM Id: " + getVmId());
- VMSnapshot result = _vmSnapshotService.creatVMSnapshot(getVmId(),getEntityId());
+ VMSnapshot result = _vmSnapshotService.creatVMSnapshot(getVmId(),getEntityId(), getQuiescevm());
if (result != null) {
VMSnapshotResponse response = _responseGenerator
.createVMSnapshotResponse(result);
diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
index e90643878ac..f6bdb3cd855 100644
--- a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
@@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.volume;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandJobType;
import org.apache.cloudstack.api.ApiConstants;
@@ -24,6 +25,7 @@ import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.PodResponse;
+import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
@@ -66,6 +68,10 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd {
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType=ZoneResponse.class,
description="the ID of the availability zone")
private Long zoneId;
+
+ @Parameter(name=ApiConstants.STORAGE_ID, type=CommandType.UUID, entityType=StoragePoolResponse.class,
+ description="the ID of the storage pool, available to ROOT admin only", since="4.3", authorized = { RoleType.Admin })
+ private Long storageId;
/////////////////////////////////////////////////////
@@ -101,6 +107,9 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd {
return zoneId;
}
+ public Long getStorageId() {
+ return storageId;
+ }
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java
index a8c6dc2f0be..b5cebf36afe 100644
--- a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java
@@ -53,6 +53,9 @@ public class CreateVpnConnectionCmd extends BaseAsyncCreateCmd {
required=true, description="id of the customer gateway")
private Long customerGatewayId;
+ @Parameter(name=ApiConstants.PASSIVE, type=CommandType.BOOLEAN, required=false, description="connection is passive or not")
+ private Boolean passive;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -65,6 +68,13 @@ public class CreateVpnConnectionCmd extends BaseAsyncCreateCmd {
public Long getCustomerGatewayId() {
return customerGatewayId;
}
+
+ public boolean isPassive() {
+ if (passive == null) {
+ return false;
+ }
+ return passive;
+ }
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
diff --git a/api/src/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java b/api/src/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java
index 99075b5f213..b4cf5e7d113 100644
--- a/api/src/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java
@@ -68,6 +68,9 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont
@SerializedName(ApiConstants.STATE) @Param(description="State of vpn connection")
private String state;
+ @SerializedName(ApiConstants.PASSIVE) @Param(description="State of vpn connection")
+ private boolean passive;
+
@SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner")
private String accountName;
@@ -141,6 +144,10 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont
this.state = state;
}
+ public void setPassive(boolean passive) {
+ this.passive = passive;
+ }
+
public void setCreated(Date created) {
this.created = created;
}
diff --git a/api/src/org/apache/cloudstack/usage/UsageService.java b/api/src/org/apache/cloudstack/usage/UsageService.java
index 383822592bc..e203c3f50c3 100755
--- a/api/src/org/apache/cloudstack/usage/UsageService.java
+++ b/api/src/org/apache/cloudstack/usage/UsageService.java
@@ -23,6 +23,8 @@ import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd;
import org.apache.cloudstack.api.command.admin.usage.GetUsageRecordsCmd;
import org.apache.cloudstack.api.response.UsageTypeResponse;
+import com.cloud.utils.Pair;
+
public interface UsageService {
/**
* Generate Billing Records from the last time it was generated to the
@@ -50,7 +52,7 @@ public interface UsageService {
* the appropriate page number)
* @return a list of usage records
*/
- List extends Usage> getUsageRecords(GetUsageRecordsCmd cmd);
+ Pair, Integer> getUsageRecords(GetUsageRecordsCmd cmd);
/**
* Retrieves the timezone used for usage aggregation. One day is represented as midnight to 11:59:59pm
diff --git a/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java b/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java
index 1f218f47e2a..b8dfb0a1e65 100644
--- a/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java
+++ b/api/test/org/apache/cloudstack/api/command/test/UsageCmdTest.java
@@ -16,7 +16,11 @@
// under the License.
package org.apache.cloudstack.api.command.test;
+import java.util.ArrayList;
+import java.util.List;
+
import junit.framework.TestCase;
+
import org.apache.cloudstack.api.command.admin.usage.GetUsageRecordsCmd;
import org.apache.cloudstack.usage.Usage;
import org.apache.cloudstack.usage.UsageService;
@@ -26,8 +30,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
-import java.util.ArrayList;
-import java.util.List;
+import com.cloud.utils.Pair;
public class UsageCmdTest extends TestCase {
@@ -56,7 +59,7 @@ public class UsageCmdTest extends TestCase {
UsageService usageService = Mockito.mock(UsageService.class);
- List usageRecords = new ArrayList();
+ Pair, Integer> usageRecords = new Pair, Integer>(new ArrayList(), new Integer(0));
Mockito.when(usageService.getUsageRecords(getUsageRecordsCmd)).thenReturn(
usageRecords);
diff --git a/awsapi/src/com/cloud/bridge/persist/dao/BaseDao.java b/awsapi/src/com/cloud/bridge/persist/dao/BaseDao.java
index 83679fa002d..3b04b5070e1 100644
--- a/awsapi/src/com/cloud/bridge/persist/dao/BaseDao.java
+++ b/awsapi/src/com/cloud/bridge/persist/dao/BaseDao.java
@@ -16,18 +16,11 @@
// under the License.
package com.cloud.bridge.persist.dao;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
import java.util.Properties;
import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.properties.EncryptableProperties;
-import com.cloud.bridge.util.ConfigurationHelper;
-import com.cloud.bridge.util.EncryptionSecretKeyCheckerUtil;
+import com.cloud.utils.db.DbProperties;
@@ -43,24 +36,9 @@ public class BaseDao {
static{
logger.info("Initializing DB props");
- File propertiesFile = ConfigurationHelper.findConfigurationFile("db.properties");
- Properties EC2Prop = null;
-
- if (null != propertiesFile) {
- if(EncryptionSecretKeyCheckerUtil.useEncryption()){
- StandardPBEStringEncryptor encryptor = EncryptionSecretKeyCheckerUtil.getEncryptor();
- EC2Prop = new EncryptableProperties(encryptor);
- } else {
- EC2Prop = new Properties();
- }
+ Properties EC2Prop = DbProperties.getDbProperties();
- try {
- EC2Prop.load( new FileInputStream( propertiesFile ));
- } catch (FileNotFoundException e) {
- logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e);
- } catch (IOException e) {
- logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e);
- }
+ if (EC2Prop.size() > 0) {
dbHost = EC2Prop.getProperty( "db.cloud.host" );
awsapi_dbName = EC2Prop.getProperty( "db.awsapi.name" );
cloud_dbName = EC2Prop.getProperty( "db.cloud.name" );
diff --git a/awsapi/src/com/cloud/bridge/util/EncryptionSecretKeyCheckerUtil.java b/awsapi/src/com/cloud/bridge/util/EncryptionSecretKeyCheckerUtil.java
deleted file mode 100644
index 6f0049f24c4..00000000000
--- a/awsapi/src/com/cloud/bridge/util/EncryptionSecretKeyCheckerUtil.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// 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.bridge.util;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.Properties;
-
-import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
-
-
-public class EncryptionSecretKeyCheckerUtil {
- private static final Logger s_logger = Logger.getLogger(EncryptionSecretKeyCheckerUtil.class);
-
- private static final String s_keyFile = "/etc/cloudstack/management/key";
- private static final String s_envKey = "CLOUD_SECRET_KEY";
- private static StandardPBEStringEncryptor s_encryptor = new StandardPBEStringEncryptor();
- private static boolean s_useEncryption = false;
-
- static{
- //Get encryption type from db.properties
- final File dbPropsFile = ConfigurationHelper.findConfigurationFile("db.properties");
- final Properties dbProps = new Properties();
- try {
- dbProps.load(new FileInputStream(dbPropsFile));
-
- final String encryptionType = dbProps.getProperty("db.cloud.encryption.type");
-
- s_logger.debug("Encryption Type: "+ encryptionType);
-
- if(encryptionType != null && !encryptionType.equals("none")){
-
- s_encryptor.setAlgorithm("PBEWithMD5AndDES");
- String secretKey = null;
-
- SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig();
-
- if(encryptionType.equals("file")){
- try {
- BufferedReader in = new BufferedReader(new FileReader(s_keyFile));
- secretKey = in.readLine();
- //Check for null or empty secret key
- } catch (FileNotFoundException e) {
- throw new RuntimeException("File containing secret key not found: "+s_keyFile, e);
- } catch (IOException e) {
- throw new RuntimeException("Error while reading secret key from: "+s_keyFile, e);
- }
-
- if(secretKey == null || secretKey.isEmpty()){
- throw new RuntimeException("Secret key is null or empty in file "+s_keyFile);
- }
-
- } else if(encryptionType.equals("env")){
- secretKey = System.getenv(s_envKey);
- if(secretKey == null || secretKey.isEmpty()){
- throw new RuntimeException("Environment variable "+s_envKey+" is not set or empty");
- }
- } else if(encryptionType.equals("web")){
- ServerSocket serverSocket = null;
- int port = 8097;
- try {
- serverSocket = new ServerSocket(port);
- } catch (IOException ioex) {
- throw new RuntimeException("Error initializing secret key reciever", ioex);
- }
- s_logger.info("Waiting for admin to send secret key on port "+port);
- Socket clientSocket = null;
- try {
- clientSocket = serverSocket.accept();
- } catch (IOException e) {
- throw new RuntimeException("Accept failed on "+port);
- }
- PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
- BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
- String inputLine, outputLine;
- if ((inputLine = in.readLine()) != null) {
- secretKey = inputLine;
- }
- out.close();
- in.close();
- clientSocket.close();
- serverSocket.close();
- } else {
- throw new RuntimeException("Invalid encryption type: "+encryptionType);
- }
-
- stringConfig.setPassword(secretKey);
- s_encryptor.setConfig(stringConfig);
- s_useEncryption = true;
- }
- } catch (FileNotFoundException e) {
- throw new RuntimeException("File db.properties not found", e);
- } catch (IOException e) {
- throw new RuntimeException("Error while reading db.properties", e);
- }
- }
-
- public static StandardPBEStringEncryptor getEncryptor() {
- return s_encryptor;
- }
-
- public static boolean useEncryption(){
- return s_useEncryption;
- }
-
- //Initialize encryptor for migration during secret key change
- public static void initEncryptorForMigration(String secretKey){
- s_encryptor.setAlgorithm("PBEWithMD5AndDES");
- SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig();
- stringConfig.setPassword(secretKey);
- s_encryptor.setConfig(stringConfig);
- s_useEncryption = true;
- }
-}
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index 12d2a11a294..d548527267b 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -1176,9 +1176,11 @@ label.virtual.machines=Virtual machines
label.virtual.network=Virtual Network
label.virtual.router=Virtual Router
label.virtual.routers=Virtual Routers
-label.vlan.id=VLAN ID
-label.vlan.range=VLAN Range
-label.vlan=VLAN
+label.vlan.id=VLAN/VNI ID
+label.vlan.range=VLAN/VNI Range
+label.vlan=VLAN/VNI
+label.vnet=VLAN/VNI
+label.vnet.id=VLAN/VNI ID
label.vxlan.id=VXLAN ID
label.vxlan.range=VXLAN Range
label.vxlan=VXLAN
diff --git a/client/pom.xml b/client/pom.xml
index 8cbdaffe94f..b6b65bad95c 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -90,6 +90,11 @@
cloud-plugin-network-nvp
${project.version}
+
+ org.apache.cloudstack
+ cloud-plugin-network-contrail
+ ${project.version}
+
org.apache.cloudstack
cloud-plugin-network-ovs
@@ -156,6 +161,11 @@
+
+ org.apache.cloudstack
+ cloud-plugin-hypervisor-hyperv
+ ${project.version}
+
org.apache.cloudstack
cloud-plugin-storage-allocator-random
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 428042a5137..8829cc5b987 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -19,9 +19,9 @@
### Please standardize naming conventions to camel-case (even for acronyms).
### Account commands
-createAccount=3
-deleteAccount=3
-updateAccount=3
+createAccount=7
+deleteAccount=7
+updateAccount=7
disableAccount=7
enableAccount=7
lockAccount=7
@@ -29,8 +29,8 @@ listAccounts=15
markDefaultZoneForAccount=1
#### User commands
-createUser=3
-deleteUser=3
+createUser=7
+deleteUser=7
updateUser=15
listUsers=7
lockUser=7
@@ -698,5 +698,6 @@ removeAccountFromAclGroup=7
grantPermissionToAclGroup=7
revokePermissionFromAclGroup=7
-
+#### juniper-contrail commands
+createServiceInstance=1
diff --git a/core/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context.xml b/core/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context.xml
index 40fcc71c14e..5ddb66f3a9c 100644
--- a/core/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context.xml
+++ b/core/resources/META-INF/cloudstack/bootstrap/spring-bootstrap-context.xml
@@ -25,8 +25,9 @@
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
- >
+ >
+
diff --git a/core/src/com/cloud/agent/api/SetupCommand.java b/core/src/com/cloud/agent/api/SetupCommand.java
index ee43c5933da..65700e9453c 100644
--- a/core/src/com/cloud/agent/api/SetupCommand.java
+++ b/core/src/com/cloud/agent/api/SetupCommand.java
@@ -23,6 +23,8 @@ public class SetupCommand extends Command {
HostEnvironment env;
boolean multipath;
boolean needSetup;
+ String secondaryStorage;
+ String systemVmIso;
public boolean needSetup() {
return needSetup;
@@ -36,6 +38,8 @@ public class SetupCommand extends Command {
this.env = env;
this.multipath = false;
this.needSetup = false;
+ secondaryStorage = null;
+ systemVmIso = null;
}
public HostEnvironment getEnvironment() {
@@ -53,6 +57,22 @@ public class SetupCommand extends Command {
return multipath;
}
+ public void setSecondaryStorage(String secondaryStorage) {
+ this.secondaryStorage = secondaryStorage;
+ }
+
+ public String getSecondaryStorage() {
+ return this.secondaryStorage;
+ }
+
+ public void setSystemVmIso(String systemVmIso) {
+ this.systemVmIso = systemVmIso;
+ }
+
+ public String getSystemVmIso() {
+ return this.systemVmIso;
+ }
+
@Override
public boolean executeInSequence() {
return true;
diff --git a/core/src/com/cloud/agent/api/VMSnapshotTO.java b/core/src/com/cloud/agent/api/VMSnapshotTO.java
index c7b42d25bc9..473c39a595d 100644
--- a/core/src/com/cloud/agent/api/VMSnapshotTO.java
+++ b/core/src/com/cloud/agent/api/VMSnapshotTO.java
@@ -16,6 +16,10 @@
// under the License.
package com.cloud.agent.api;
+import java.util.List;
+
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+
import com.cloud.vm.snapshot.VMSnapshot;
public class VMSnapshotTO {
@@ -26,6 +30,7 @@ public class VMSnapshotTO {
private Boolean current;
private String description;
private VMSnapshotTO parent;
+ private List volumes;
public Long getId() {
return id;
@@ -87,4 +92,11 @@ public class VMSnapshotTO {
this.parent = parent;
}
+ public List getVolumes() {
+ return this.volumes;
+ }
+
+ public void setVolumes(List volumes) {
+ this.volumes = volumes;
+ }
}
diff --git a/core/src/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java b/core/src/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java
index 83163039f51..d54c29c9c86 100644
--- a/core/src/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java
+++ b/core/src/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java
@@ -30,6 +30,7 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand {
private long ikeLifetime;
private long espLifetime;
private boolean dpd;
+ private boolean passive;
@Override
public boolean executeInSequence() {
@@ -41,7 +42,7 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand {
}
public Site2SiteVpnCfgCommand (boolean create, String localPublicIp, String localPublicGateway, String localGuestCidr, String peerGatewayIp,
- String peerGuestCidrList, String ikePolicy, String espPolicy, String ipsecPsk, Long ikeLifetime, Long espLifetime, Boolean dpd) {
+ String peerGuestCidrList, String ikePolicy, String espPolicy, String ipsecPsk, Long ikeLifetime, Long espLifetime, Boolean dpd, boolean passive) {
this.create = create;
this.setLocalPublicIp(localPublicIp);
this.setLocalPublicGateway(localPublicGateway);
@@ -54,6 +55,7 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand {
this.ikeLifetime = ikeLifetime;
this.espLifetime = espLifetime;
this.dpd = dpd;
+ this.passive = passive;
}
public boolean isCreate() {
@@ -151,4 +153,12 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand {
public void setPeerGuestCidrList(String peerGuestCidrList) {
this.peerGuestCidrList = peerGuestCidrList;
}
+
+ public boolean isPassive() {
+ return passive;
+ }
+
+ public void setPassive(boolean passive) {
+ this.passive = passive;
+ }
}
diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
index 874146c6258..4c2ee505117 100755
--- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
+++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
@@ -761,6 +761,9 @@ public class VirtualRoutingResource implements Manager {
} else {
args += "0";
}
+ if (cmd.isPassive()) {
+ args += " -p ";
+ }
} else {
args = "-D";
args += " -r ";
diff --git a/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java b/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java
index b43722a6418..4efeafd9aa1 100644
--- a/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java
+++ b/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java
@@ -18,6 +18,8 @@
*/
package com.cloud.storage.resource;
+import org.apache.log4j.Logger;
+
import org.apache.cloudstack.storage.command.AttachCommand;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
@@ -26,7 +28,6 @@ import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
-import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
@@ -67,7 +68,9 @@ public class StorageSubsystemCommandHandlerBase implements StorageSubsystemComma
DataStoreTO srcDataStore = srcData.getDataStore();
DataStoreTO destDataStore = destData.getDataStore();
- if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcData.getDataStore().getRole() == DataStoreRole.Image && destData.getDataStore().getRole() == DataStoreRole.Primary) {
+ if (srcData.getObjectType() == DataObjectType.TEMPLATE
+ && (srcData.getDataStore().getRole() == DataStoreRole.Image || srcData.getDataStore().getRole() == DataStoreRole.ImageCache)
+ && destData.getDataStore().getRole() == DataStoreRole.Primary) {
//copy template to primary storage
return processor.copyTemplateToPrimaryStorage(cmd);
} else if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcDataStore.getRole() == DataStoreRole.Primary && destDataStore.getRole() == DataStoreRole.Primary) {
diff --git a/core/src/org/apache/cloudstack/storage/to/ImageStoreTO.java b/core/src/org/apache/cloudstack/storage/to/ImageStoreTO.java
index ec6c24092d3..c9bcf5b7b47 100644
--- a/core/src/org/apache/cloudstack/storage/to/ImageStoreTO.java
+++ b/core/src/org/apache/cloudstack/storage/to/ImageStoreTO.java
@@ -83,6 +83,11 @@ public class ImageStoreTO implements DataStoreTO {
return uuid;
}
+ @Override
+ public String getUrl() {
+ return getUri();
+ }
+
public void setUuid(String uuid) {
this.uuid = uuid;
}
diff --git a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
index 91d78a49350..3141c3f1026 100644
--- a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
+++ b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java
@@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.storage.to;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
import com.cloud.agent.api.to.DataStoreTO;
@@ -31,8 +32,9 @@ public class PrimaryDataStoreTO implements DataStoreTO {
private String host;
private String path;
private int port;
+ private final String url;
- public PrimaryDataStoreTO(PrimaryDataStoreInfo dataStore) {
+ public PrimaryDataStoreTO(PrimaryDataStore dataStore) {
this.uuid = dataStore.getUuid();
this.name = dataStore.getName();
this.id = dataStore.getId();
@@ -40,6 +42,7 @@ public class PrimaryDataStoreTO implements DataStoreTO {
this.setHost(dataStore.getHostAddress());
this.setPath(dataStore.getPath());
this.setPort(dataStore.getPort());
+ this.url = dataStore.getUri();
}
public long getId() {
@@ -51,6 +54,11 @@ public class PrimaryDataStoreTO implements DataStoreTO {
return this.uuid;
}
+ @Override
+ public String getUrl() {
+ return this.url;
+ }
+
public String getName() {
return this.name;
}
diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStore.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java
similarity index 96%
rename from engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStore.java
rename to engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java
index d8b2a734792..b4d7cb911c8 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStore.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStore.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.cloudstack.storage.datastore;
+package org.apache.cloudstack.engine.subsystem.api.storage;
import java.util.List;
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotOptions.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotOptions.java
new file mode 100644
index 00000000000..84a440d7bfb
--- /dev/null
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotOptions.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.engine.subsystem.api.storage;
+
+public class VMSnapshotOptions {
+ private final boolean quiesceVM;
+ public VMSnapshotOptions(boolean quiesceVM) {
+ this.quiesceVM = quiesceVM;
+ }
+
+ public boolean needQuiesceVM() {
+ return quiesceVM;
+ }
+}
diff --git a/engine/orchestration/src/com/cloud/agent/manager/AgentAttache.java b/engine/orchestration/src/com/cloud/agent/manager/AgentAttache.java
index ff35255c7db..9c87812dcc9 100755
--- a/engine/orchestration/src/com/cloud/agent/manager/AgentAttache.java
+++ b/engine/orchestration/src/com/cloud/agent/manager/AgentAttache.java
@@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.log4j.Logger;
@@ -107,7 +108,8 @@ public abstract class AgentAttache {
protected Long _currentSequence;
protected Status _status = Status.Connecting;
protected boolean _maintenance;
- protected long _nextSequence;
+ protected long _nextSequence;
+ protected AtomicInteger _outstandingTaskCount;
protected AgentManagerImpl _agentMgr;
@@ -131,6 +133,7 @@ public abstract class AgentAttache {
_requests = new LinkedList();
_agentMgr = agentMgr;
_nextSequence = s_rand.nextInt(Short.MAX_VALUE) << 48;
+ _outstandingTaskCount = new AtomicInteger(0);
}
public synchronized long getNextSequence() {
diff --git a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java
index 3e684cc9fd4..39d470213a9 100755
--- a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java
@@ -159,24 +159,28 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
protected ScheduledExecutorService _directAgentExecutor;
protected ScheduledExecutorService _monitorExecutor;
+ private int _directAgentThreadCap;
+
protected StateMachine2 _statusStateMachine = Status.getStateMachine();
private final Map _pingMap = new ConcurrentHashMap(10007);
@Inject ResourceManager _resourceMgr;
- protected final ConfigKey Workers = new ConfigKey(Integer.class, "workers", "Advance", "5",
+ protected final ConfigKey Workers = new ConfigKey(Integer.class, "workers", "Advanced", "5",
"Number of worker threads handling remote agent connections.", false);
- protected final ConfigKey Port = new ConfigKey(Integer.class, "port", "Advance", "8250", "Port to listen on for remote agent connections.", false);
- protected final ConfigKey PingInterval = new ConfigKey(Integer.class, "ping.interval", "Advance", "60",
+ protected final ConfigKey Port = new ConfigKey(Integer.class, "port", "Advanced", "8250", "Port to listen on for remote agent connections.", false);
+ protected final ConfigKey PingInterval = new ConfigKey(Integer.class, "ping.interval", "Advanced", "60",
"Interval to send application level pings to make sure the connection is still working", false);
- protected final ConfigKey PingTimeout = new ConfigKey(Float.class, "ping.timeout", "Advance", "2.5",
+ protected final ConfigKey PingTimeout = new ConfigKey(Float.class, "ping.timeout", "Advanced", "2.5",
"Multiplier to ping.interval before announcing an agent has timed out", true);
- protected final ConfigKey AlertWait = new ConfigKey(Integer.class, "alert.wait", "Advance", "1800",
+ protected final ConfigKey AlertWait = new ConfigKey(Integer.class, "alert.wait", "Advanced", "1800",
"Seconds to wait before alerting on a disconnected agent", true);
- protected final ConfigKey DirectAgentLoadSize = new ConfigKey(Integer.class, "direct.agent.load.size", "Advance", "16",
+ protected final ConfigKey DirectAgentLoadSize = new ConfigKey(Integer.class, "direct.agent.load.size", "Advanced", "16",
"The number of direct agents to load each time", false);
- protected final ConfigKey DirectAgentPoolSize = new ConfigKey(Integer.class, "direct.agent.pool.size", "Advance", "500",
+ protected final ConfigKey DirectAgentPoolSize = new ConfigKey(Integer.class, "direct.agent.pool.size", "Advanced", "500",
"Default size for DirectAgentPool", false);
+ protected final ConfigKey DirectAgentThreadCap = new ConfigKey(Float.class, "direct.agent.thread.cap", "Advanced", "0.1",
+ "Percentage (as a value between 0 and 1) of direct.agent.pool.size to be used as upper thread cap for a single direct agent to process requests", false);
@Override
public boolean configure(final String name, final Map params) throws ConfigurationException {
@@ -202,10 +206,10 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
_connection = new NioServer("AgentManager", Port.value(), Workers.value() + 10, this);
s_logger.info("Listening on " + Port.value() + " with " + Workers.value() + " workers");
-
_directAgentExecutor = new ScheduledThreadPoolExecutor(DirectAgentPoolSize.value(), new NamedThreadFactory("DirectAgent"));
s_logger.debug("Created DirectAgentAttache pool with size: " + DirectAgentPoolSize.value());
-
+ _directAgentThreadCap = Math.round(DirectAgentPoolSize.value() * DirectAgentThreadCap.value()) + 1; // add 1 to always make the value > 0
+
_monitorExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AgentMonitor"));
return true;
@@ -1422,6 +1426,10 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
return _directAgentExecutor;
}
+ public int getDirectAgentThreadCap() {
+ return _directAgentThreadCap;
+ }
+
public Long getAgentPingTime(long agentId) {
return _pingMap.get(agentId);
}
@@ -1568,7 +1576,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
@Override
public ConfigKey>[] getConfigKeys() {
- return new ConfigKey>[] {Workers, Port, PingInterval, PingTimeout, Wait, AlertWait, DirectAgentLoadSize, DirectAgentPoolSize};
+ return new ConfigKey>[] {Workers, Port, PingInterval, PingTimeout, Wait, AlertWait, DirectAgentLoadSize, DirectAgentPoolSize, DirectAgentThreadCap};
}
}
diff --git a/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java b/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java
index 7d3f7659639..0b6a0116214 100755
--- a/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java
+++ b/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java
@@ -132,6 +132,11 @@ public class DirectAgentAttache extends AgentAttache {
@Override
protected synchronized void runInContext() {
try {
+ if (_outstandingTaskCount.incrementAndGet() > _agentMgr.getDirectAgentThreadCap()) {
+ s_logger.warn("Task execution for direct attache(" + _id + ") has reached maximum outstanding limit(" + _agentMgr.getDirectAgentThreadCap() + "), bailing out");
+ return;
+ }
+
ServerResource resource = _resource;
if (resource != null) {
@@ -156,6 +161,8 @@ public class DirectAgentAttache extends AgentAttache {
}
} catch (Exception e) {
s_logger.warn("Unable to complete the ping task", e);
+ } finally {
+ _outstandingTaskCount.decrementAndGet();
}
}
}
@@ -168,10 +175,32 @@ public class DirectAgentAttache extends AgentAttache {
_req = req;
}
+ private void bailout() {
+ long seq = _req.getSequence();
+ try {
+ Command[] cmds = _req.getCommands();
+ ArrayList answers = new ArrayList(cmds.length);
+ for (Command cmd : cmds) {
+ Answer answer = new Answer(cmd, false, "Bailed out as maximum oustanding task limit reached");
+ answers.add(answer);
+ }
+ Response resp = new Response(_req, answers.toArray(new Answer[answers.size()]));
+ processAnswers(seq, resp);
+ } catch (Exception e) {
+ s_logger.warn(log(seq, "Exception caught in bailout "), e);
+ }
+ }
+
@Override
protected void runInContext() {
long seq = _req.getSequence();
try {
+ if (_outstandingTaskCount.incrementAndGet() > _agentMgr.getDirectAgentThreadCap()) {
+ s_logger.warn("Task execution for direct attache(" + _id + ") has reached maximum outstanding limit(" + _agentMgr.getDirectAgentThreadCap() + "), bailing out");
+ bailout();
+ return;
+ }
+
ServerResource resource = _resource;
Command[] cmds = _req.getCommands();
boolean stopOnError = _req.stopOnError();
@@ -186,7 +215,7 @@ public class DirectAgentAttache extends AgentAttache {
if (resource != null) {
answer = resource.executeRequest(cmds[i]);
if(answer == null) {
- s_logger.warn("Resource returned null answer!");
+ s_logger.warn("Resource returned null answer!");
answer = new Answer(cmds[i], false, "Resource returned null answer");
}
} else {
@@ -213,6 +242,8 @@ public class DirectAgentAttache extends AgentAttache {
processAnswers(seq, resp);
} catch (Exception e) {
s_logger.warn(log(seq, "Exception caught "), e);
+ } finally {
+ _outstandingTaskCount.decrementAndGet();
}
}
}
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 5636e0783b4..ddec9fcd7e4 100755
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -2187,7 +2187,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
}
}
});
- return false;
+ return true;
} catch ( CloudRuntimeException e ) {
s_logger.error("Failed to delete network", e);
return false;
diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 0821c81f71a..8d841d8fd1b 100644
--- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -1222,8 +1222,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
public void updateVolumeDiskChain(long volumeId, String path, String chainInfo) {
VolumeVO vol = _volsDao.findById(volumeId);
boolean needUpdate = false;
+ // Volume path is not getting updated in the DB, need to find reason and fix the issue.
+ if (vol.getPath() == null)
+ return;
if(!vol.getPath().equalsIgnoreCase(path))
- needUpdate = true;
+ needUpdate = true;
if(chainInfo != null && (vol.getChainInfo() == null || !chainInfo.equalsIgnoreCase(vol.getChainInfo())))
needUpdate = true;
diff --git a/engine/schema/src/com/cloud/network/dao/Site2SiteVpnConnectionVO.java b/engine/schema/src/com/cloud/network/dao/Site2SiteVpnConnectionVO.java
index f8eeb8a9912..d99823f2e0a 100644
--- a/engine/schema/src/com/cloud/network/dao/Site2SiteVpnConnectionVO.java
+++ b/engine/schema/src/com/cloud/network/dao/Site2SiteVpnConnectionVO.java
@@ -66,15 +66,19 @@ public class Site2SiteVpnConnectionVO implements Site2SiteVpnConnection, Interna
@Column(name=GenericDao.REMOVED_COLUMN)
private Date removed;
+ @Column(name="passive")
+ private boolean passive;
+
public Site2SiteVpnConnectionVO() { }
- public Site2SiteVpnConnectionVO(long accountId, long domainId, long vpnGatewayId, long customerGatewayId) {
+ public Site2SiteVpnConnectionVO(long accountId, long domainId, long vpnGatewayId, long customerGatewayId, boolean passive) {
this.uuid = UUID.randomUUID().toString();
this.setVpnGatewayId(vpnGatewayId);
this.setCustomerGatewayId(customerGatewayId);
this.setState(State.Pending);
this.accountId = accountId;
this.domainId = domainId;
+ this.passive = passive;
}
@Override
@@ -140,4 +144,12 @@ public class Site2SiteVpnConnectionVO implements Site2SiteVpnConnection, Interna
public long getAccountId() {
return accountId;
}
+
+ public boolean isPassive() {
+ return passive;
+ }
+
+ public void setPassive(boolean passive) {
+ this.passive = passive;
+ }
}
diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java
index df7cfd4a749..1bdd09f9ab6 100755
--- a/engine/schema/src/com/cloud/storage/VolumeVO.java
+++ b/engine/schema/src/com/cloud/storage/VolumeVO.java
@@ -146,7 +146,7 @@ public class VolumeVO implements Volume {
private Storage.ImageFormat format;
@Column(name = "display_volume", updatable = true, nullable = false)
- protected boolean displayVolume;
+ protected boolean displayVolume = true;
@Column(name = "iscsi_name")
private String _iScsiName;
diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseCreator.java b/engine/schema/src/com/cloud/upgrade/DatabaseCreator.java
index 8260aa125e5..e8f2cc5663b 100755
--- a/engine/schema/src/com/cloud/upgrade/DatabaseCreator.java
+++ b/engine/schema/src/com/cloud/upgrade/DatabaseCreator.java
@@ -18,7 +18,12 @@
*/
package com.cloud.upgrade;
-import java.io.*;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@@ -33,7 +38,6 @@ import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.component.SystemIntegrityChecker;
import com.cloud.utils.db.ScriptRunner;
-import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionLegacy;
// Creates the CloudStack Database by using the 4.0 schema and apply
@@ -173,7 +177,9 @@ public class DatabaseCreator {
try {
TransactionLegacy.initDataSource(dbPropsFile);
- } catch (NullPointerException e) {
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
}
initDB(dbPropsFile, rootPassword, databases, dryRun);
diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade307to410.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade307to410.java
index aeeb6d5863f..3edc890d603 100644
--- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade307to410.java
+++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade307to410.java
@@ -18,21 +18,16 @@
package com.cloud.upgrade.dao;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
-import com.cloud.utils.PropertiesUtil;
-import com.cloud.utils.crypt.EncryptionSecretKeyChecker;
import org.apache.log4j.Logger;
+import com.cloud.utils.db.DbProperties;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.properties.EncryptableProperties;
public class Upgrade307to410 implements DbUpgrade {
final static Logger s_logger = Logger.getLogger(Upgrade307to410.class);
@@ -68,23 +63,7 @@ public class Upgrade307to410 implements DbUpgrade {
}
private void updateRegionEntries(Connection conn) {
- File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
- final Properties dbProps;
- if (EncryptionSecretKeyChecker.useEncryption()) {
- StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor();
- dbProps = new EncryptableProperties(encryptor);
- } else {
- dbProps = new Properties();
- }
- try {
- dbProps.load(new FileInputStream(dbPropsFile));
- } catch (IOException e) {
- s_logger.fatal("Unable to load db properties file, pl. check the classpath and file path configuration", e);
- return;
- } catch (NullPointerException e) {
- s_logger.fatal("Unable to locate db properties file within classpath or absolute path: db.properties");
- return;
- }
+ final Properties dbProps = DbProperties.getDbProperties();
int region_id = 1;
String regionId = dbProps.getProperty("region.id");
if(regionId != null){
diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade40to41.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade40to41.java
index 79ca5e1fdb1..86fe589bb18 100644
--- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade40to41.java
+++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade40to41.java
@@ -19,9 +19,11 @@ package com.cloud.upgrade.dao;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.crypt.EncryptionSecretKeyChecker;
+import com.cloud.utils.db.DbProperties;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
+
import org.apache.log4j.Logger;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.properties.EncryptableProperties;
@@ -81,23 +83,7 @@ public class Upgrade40to41 implements DbUpgrade {
}
private void updateRegionEntries(Connection conn) {
- File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
- final Properties dbProps;
- if (EncryptionSecretKeyChecker.useEncryption()) {
- StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor();
- dbProps = new EncryptableProperties(encryptor);
- } else {
- dbProps = new Properties();
- }
- try {
- dbProps.load(new FileInputStream(dbPropsFile));
- } catch (IOException e) {
- s_logger.fatal("Unable to load db properties file, pl. check the classpath and file path configuration", e);
- return;
- } catch (NullPointerException e) {
- s_logger.fatal("Unable to locate db properties file within classpath or absolute path: db.properties");
- return;
- }
+ final Properties dbProps = DbProperties.getDbProperties();
int region_id = 1;
String regionId = dbProps.getProperty("region.id");
if(regionId != null){
diff --git a/engine/schema/src/com/cloud/usage/dao/UsageDao.java b/engine/schema/src/com/cloud/usage/dao/UsageDao.java
index f571b63ce40..b8335773041 100644
--- a/engine/schema/src/com/cloud/usage/dao/UsageDao.java
+++ b/engine/schema/src/com/cloud/usage/dao/UsageDao.java
@@ -22,13 +22,14 @@ import com.cloud.usage.UsageVO;
import com.cloud.user.AccountVO;
import com.cloud.user.UserStatisticsVO;
import com.cloud.user.VmDiskStatisticsVO;
+import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.SearchCriteria;
public interface UsageDao extends GenericDao {
void deleteRecordsForAccount(Long accountId);
- List searchAllRecords(SearchCriteria sc, Filter filter);
+ Pair, Integer> searchAndCountAllRecords(SearchCriteria sc, Filter filter);
void saveAccounts(List accounts);
void updateAccounts(List accounts);
diff --git a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
index e1e843e9ed5..c4d8ec4d6d4 100644
--- a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
+++ b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
@@ -35,6 +35,7 @@ import com.cloud.user.AccountVO;
import com.cloud.user.UserStatisticsVO;
import com.cloud.user.VmDiskStatisticsVO;
import com.cloud.utils.DateUtil;
+import com.cloud.utils.Pair;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchCriteria;
@@ -93,8 +94,8 @@ public class UsageDaoImpl extends GenericDaoBase implements Usage
}
@Override
- public List searchAllRecords(SearchCriteria sc, Filter filter) {
- return listIncludingRemovedBy(sc, filter);
+ public Pair, Integer> searchAndCountAllRecords(SearchCriteria sc, Filter filter) {
+ return listAndCountIncludingRemovedBy(sc, filter);
}
@Override
diff --git a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java
index 477148cfa3b..a888c123bd8 100644
--- a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java
+++ b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java
@@ -31,6 +31,9 @@ import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
+import javax.persistence.Transient;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotOptions;
import com.cloud.utils.db.GenericDao;
@@ -90,7 +93,18 @@ public class VMSnapshotVO implements VMSnapshot {
@Column(name="update_count", updatable = true, nullable=false)
protected long updatedCount;
-
+
+ @Transient
+ VMSnapshotOptions options;
+
+ public VMSnapshotOptions getOptions() {
+ return options;
+ }
+
+ public void setOptions(VMSnapshotOptions options) {
+ this.options = options;
+ }
+
public Long getParent() {
return parent;
}
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
index a451ca47288..67cc324bc5c 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
@@ -25,7 +25,6 @@ import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
-import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
@@ -46,10 +45,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.command.CopyCommand;
-import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
import com.cloud.agent.api.Answer;
@@ -62,19 +57,11 @@ import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.configuration.Config;
import com.cloud.host.Host;
-import com.cloud.host.dao.HostDao;
import com.cloud.server.ManagementService;
import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.DiskOfferingDao;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao;
-import com.cloud.storage.snapshot.SnapshotManager;
-import com.cloud.template.TemplateManager;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
@@ -177,7 +164,13 @@ AncientDataMotionStrategy implements DataMotionStrategy {
CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), destData.getTO(), _primaryStorageDownloadWait, _mgmtServer.getExecuteInSequence());
EndPoint ep = selector.select(srcForCopy, destData);
- answer = ep.sendMessage(cmd);
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (cacheData != null) {
if (srcData.getType() == DataObjectType.VOLUME && destData.getType() == DataObjectType.VOLUME) {
@@ -255,7 +248,14 @@ AncientDataMotionStrategy implements DataMotionStrategy {
}
CopyCommand cmd = new CopyCommand(srcData.getTO(), volObj.getTO(), _createVolumeFromSnapshotWait, _mgmtServer.getExecuteInSequence());
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
return answer;
} catch (Exception e) {
@@ -273,7 +273,14 @@ AncientDataMotionStrategy implements DataMotionStrategy {
CopyCommand cmd = new CopyCommand(template.getTO(), volume.getTO(), 0, _mgmtServer.getExecuteInSequence());
try {
EndPoint ep = selector.select(volume.getDataStore());
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
return answer;
} catch (Exception e) {
s_logger.debug("Failed to send to storage pool", e);
@@ -315,7 +322,13 @@ AncientDataMotionStrategy implements DataMotionStrategy {
CopyCommand cmd = new CopyCommand(objOnImageStore.getTO(), destData.getTO(), _copyvolumewait, _mgmtServer.getExecuteInSequence());
EndPoint ep = selector.select(objOnImageStore, destData);
- answer = ep.sendMessage(cmd);
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (answer == null || !answer.getResult()) {
if (answer != null) {
@@ -333,7 +346,14 @@ AncientDataMotionStrategy implements DataMotionStrategy {
DataObject cacheData = cacheMgr.createCacheObject(srcData, destScope);
CopyCommand cmd = new CopyCommand(cacheData.getTO(), destData.getTO(), _copyvolumewait, _mgmtServer.getExecuteInSequence());
EndPoint ep = selector.select(cacheData, destData);
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
// delete volume on cache store
if (cacheData != null) {
cacheMgr.deleteCacheObject(cacheData);
@@ -348,7 +368,14 @@ AncientDataMotionStrategy implements DataMotionStrategy {
StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destData.getDataStore().getId(), DataStoreRole.Primary);
MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool);
EndPoint ep = selector.select(volume.getDataStore());
- MigrateVolumeAnswer answer = (MigrateVolumeAnswer) ep.sendMessage(command);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(command, false, errMsg);
+ } else {
+ answer = ep.sendMessage(command);
+ }
if (answer == null || !answer.getResult()) {
throw new CloudRuntimeException("Failed to migrate volume " + volume + " to storage pool " + destPool);
@@ -356,7 +383,7 @@ AncientDataMotionStrategy implements DataMotionStrategy {
// Update the volume details after migration.
VolumeVO volumeVo = volDao.findById(volume.getId());
Long oldPoolId = volume.getPoolId();
- volumeVo.setPath(answer.getVolumePath());
+ volumeVo.setPath(((MigrateVolumeAnswer)answer).getVolumePath());
volumeVo.setFolder(destPool.getPath());
volumeVo.setPodId(destPool.getPodId());
volumeVo.setPoolId(destPool.getId());
@@ -431,7 +458,14 @@ AncientDataMotionStrategy implements DataMotionStrategy {
}
CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _createprivatetemplatefromsnapshotwait, _mgmtServer.getExecuteInSequence());
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
// clean up snapshot copied to staging
if (needCache && srcData != null) {
@@ -455,11 +489,23 @@ AncientDataMotionStrategy implements DataMotionStrategy {
CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait, _mgmtServer.getExecuteInSequence());
cmd.setCacheTO(cacheData.getTO());
EndPoint ep = selector.select(srcData, destData);
- answer = ep.sendMessage(cmd);
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
} else {
CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait, _mgmtServer.getExecuteInSequence());
EndPoint ep = selector.select(srcData, destData);
- answer = ep.sendMessage(cmd);
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
}
// clean up cache entry in case of failure
if (answer == null || !answer.getResult()) {
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
index ce6198dc21e..308347d8a38 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
@@ -172,12 +172,26 @@ public class TemplateServiceImpl implements TemplateService {
return;
}
- TemplateOpContext context = new TemplateOpContext(callback,
- templateOnStore, null);
+ try {
+ TemplateOpContext context = new TemplateOpContext(callback,
+ templateOnStore, null);
- AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
- caller.setCallback(caller.getTarget().createTemplateCallback(null, null)).setContext(context);
- store.getDriver().createAsync(store, templateOnStore, caller);
+ AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
+ caller.setCallback(caller.getTarget().createTemplateCallback(null, null)).setContext(context);
+ store.getDriver().createAsync(store, templateOnStore, caller);
+ } catch (CloudRuntimeException ex) {
+ // clean up already persisted template_store_ref entry in case of createTemplateCallback is never called
+ TemplateDataStoreVO templateStoreVO = _vmTemplateStoreDao.findByStoreTemplate(store.getId(), template.getId());
+ if (templateStoreVO != null) {
+ TemplateInfo tmplObj = _templateFactory.getTemplate(template, store);
+ tmplObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
+ }
+ TemplateApiResult result = new TemplateApiResult(template);
+ result.setResult(ex.getMessage());
+ if (callback != null) {
+ callback.complete(result);
+ }
+ }
}
@Override
@@ -441,7 +455,14 @@ public class TemplateServiceImpl implements TemplateService {
tmplTO.setId(tInfo.getId());
DeleteCommand dtCommand = new DeleteCommand(tmplTO);
EndPoint ep = _epSelector.select(store);
- Answer answer = ep.sendMessage(dtCommand);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(dtCommand, false, errMsg);
+ } else {
+ answer = ep.sendMessage(dtCommand);
+ }
if (answer == null || !answer.getResult()) {
s_logger.info("Failed to deleted template at store: " + store.getName());
@@ -513,7 +534,14 @@ public class TemplateServiceImpl implements TemplateService {
private Map listTemplate(DataStore ssStore) {
ListTemplateCommand cmd = new ListTemplateCommand(ssStore.getTO());
EndPoint ep = _epSelector.select(ssStore);
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (answer != null && answer.getResult()) {
ListTemplateAnswer tanswer = (ListTemplateAnswer) answer;
return tanswer.getTemplateInfo();
@@ -718,11 +746,23 @@ public class TemplateServiceImpl implements TemplateService {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Invoke datastore driver createAsync to create template on destination store");
}
- TemplateOpContext context = new TemplateOpContext(null,
- (TemplateObject) templateOnStore, future);
- AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
- caller.setCallback(caller.getTarget().copyTemplateCrossZoneCallBack(null, null)).setContext(context);
- destStore.getDriver().createAsync(destStore, templateOnStore, caller);
+ try {
+ TemplateOpContext context = new TemplateOpContext(null,
+ (TemplateObject)templateOnStore, future);
+ AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
+ caller.setCallback(caller.getTarget().copyTemplateCrossZoneCallBack(null, null)).setContext(context);
+ destStore.getDriver().createAsync(destStore, templateOnStore, caller);
+ } catch (CloudRuntimeException ex) {
+ // clean up already persisted template_store_ref entry in case of createTemplateCallback is never called
+ TemplateDataStoreVO templateStoreVO = _vmTemplateStoreDao.findByStoreTemplate(destStore.getId(), srcTemplate.getId());
+ if (templateStoreVO != null) {
+ TemplateInfo tmplObj = _templateFactory.getTemplate(srcTemplate, destStore);
+ tmplObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
+ }
+ TemplateApiResult res = new TemplateApiResult((TemplateObject)templateOnStore);
+ res.setResult(ex.getMessage());
+ future.complete(res);
+ }
return future;
}
diff --git a/engine/storage/integration-test/pom.xml b/engine/storage/integration-test/pom.xml
index dc4065e5988..4e5fdeb7eb9 100644
--- a/engine/storage/integration-test/pom.xml
+++ b/engine/storage/integration-test/pom.xml
@@ -185,5 +185,38 @@
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+
+
+ maven-antrun-plugin
+
+ [1.7,)
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/storage/integration-test/test/resources/storageContext.xml b/engine/storage/integration-test/test/resources/storageContext.xml
index 0dcd6a83407..c9845157afd 100644
--- a/engine/storage/integration-test/test/resources/storageContext.xml
+++ b/engine/storage/integration-test/test/resources/storageContext.xml
@@ -85,10 +85,7 @@
-<<<<<<< HEAD
-=======
->>>>>>> pluggable_vm_snapshot
diff --git a/engine/storage/resources/META-INF/cloudstack/core/spring-engine-storage-core-context.xml b/engine/storage/resources/META-INF/cloudstack/core/spring-engine-storage-core-context.xml
index 76ec23e37d1..8040d801b3e 100644
--- a/engine/storage/resources/META-INF/cloudstack/core/spring-engine-storage-core-context.xml
+++ b/engine/storage/resources/META-INF/cloudstack/core/spring-engine-storage-core-context.xml
@@ -38,6 +38,9 @@
+
+
diff --git a/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml b/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml
index d25aeea0250..72997509130 100644
--- a/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml
+++ b/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml
@@ -33,6 +33,4 @@
-
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
index 0799721312d..9b73bea5211 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
+++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java
@@ -40,7 +40,7 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.framework.async.AsyncRpcContext;
import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
-import org.apache.cloudstack.storage.datastore.PrimaryDataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.log4j.Logger;
diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java
index 8613668b180..bb7911df811 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java
@@ -19,6 +19,7 @@
package org.apache.cloudstack.storage.datastore;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
public interface PrimaryDataStoreProviderManager {
diff --git a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
index 196b08b2f42..ce83790a4f5 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
@@ -27,15 +27,17 @@ import java.util.List;
import javax.inject.Inject;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.storage.LocalHostEndpoint;
import org.apache.cloudstack.storage.RemoteHostEndPoint;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
@@ -43,10 +45,10 @@ import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.ScopeType;
+import com.cloud.storage.Storage.TemplateType;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.QueryBuilder;
import com.cloud.utils.db.SearchCriteria.Op;
-import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
@@ -185,6 +187,16 @@ public class DefaultEndPointSelector implements EndPointSelector {
selectedStore = destStore;
}
EndPoint ep = findEndpointForImageStorage(selectedStore);
+ if (ep != null) {
+ return ep;
+ }
+ // handle special case where it is used in deploying ssvm for S3
+ if (srcData instanceof TemplateInfo) {
+ TemplateInfo tmpl = (TemplateInfo)srcData;
+ if (tmpl.getTemplateType() == TemplateType.SYSTEM) {
+ ep = LocalHostEndpoint.getEndpoint();
+ }
+ }
return ep;
} else if (moveBetweenImages(srcStore, destStore)) {
EndPoint ep = findEndpointForImageStorage(destStore);
@@ -209,9 +221,7 @@ public class DefaultEndPointSelector implements EndPointSelector {
// we can arbitrarily pick one ssvm to do that task
List ssAHosts = listUpAndConnectingSecondaryStorageVmHost(dcId);
if (ssAHosts == null || ssAHosts.isEmpty()) {
- s_logger.info("No running ssvm is found, so command will be sent to LocalHostEndPoint");
- return LocalHostEndpoint.getEndpoint(); // use local host as endpoint in
- // case of no ssvm existing
+ return null;
}
Collections.shuffle(ssAHosts);
HostVO host = ssAHosts.get(0);
@@ -232,7 +242,16 @@ public class DefaultEndPointSelector implements EndPointSelector {
@Override
public EndPoint select(DataObject object) {
DataStore store = object.getDataStore();
- return select(store);
+ EndPoint ep = select(store);
+ if (ep != null)
+ return ep;
+ if (object instanceof TemplateInfo) {
+ TemplateInfo tmplInfo = (TemplateInfo)object;
+ if (store.getScope().getScopeType() == ScopeType.ZONE && store.getScope().getScopeId() == null && tmplInfo.getTemplateType() == TemplateType.SYSTEM) {
+ return LocalHostEndpoint.getEndpoint(); // for bootstrap system vm template downloading to region image store
+ }
+ }
+ return null;
}
@Override
diff --git a/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelper.java b/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelper.java
index 40ced1d832c..f22659947ee 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelper.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelper.java
@@ -18,14 +18,15 @@
*/
package org.apache.cloudstack.storage.helper;
-import com.cloud.agent.api.to.DataTO;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
-import org.apache.cloudstack.storage.to.SnapshotObjectTO;
+
+import com.cloud.agent.api.VMSnapshotTO;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.vm.VirtualMachine;
public interface HypervisorHelper {
DataTO introduceObject(DataTO object, Scope scope, Long storeId);
boolean forgetObject(DataTO object, Scope scope, Long storeId);
- SnapshotObjectTO takeSnapshot(SnapshotObjectTO snapshotObjectTO, Scope scope);
- boolean revertSnapshot(SnapshotObjectTO snapshotObjectTO, Scope scope);
+ VMSnapshotTO quiesceVm(VirtualMachine virtualMachine);
+ boolean unquiesceVM(VirtualMachine virtualMachine, VMSnapshotTO vmSnapshotTO);
}
diff --git a/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java b/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java
index 81e6f7c69c5..7dbd35cf254 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java
@@ -18,30 +18,63 @@
*/
package org.apache.cloudstack.storage.helper;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.to.DataTO;
-import com.cloud.utils.exception.CloudRuntimeException;
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectAnswer;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
-import org.apache.cloudstack.storage.to.SnapshotObjectTO;
-import org.apache.log4j.Logger;
+import org.apache.cloudstack.storage.vmsnapshot.VMSnapshotHelper;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
-import javax.inject.Inject;
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
+import com.cloud.agent.api.DeleteVMSnapshotCommand;
+import com.cloud.agent.api.VMSnapshotTO;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.snapshot.VMSnapshot;
public class HypervisorHelperImpl implements HypervisorHelper {
private static final Logger s_logger = Logger.getLogger(HypervisorHelperImpl.class);
@Inject
EndPointSelector selector;
-
+ @Inject
+ VMSnapshotHelper vmSnapshotHelper;
+ @Inject
+ GuestOSDao guestOSDao;
+ @Inject
+ ConfigurationDao configurationDao;
+ @Inject
+ AgentManager agentMgr;
@Override
public DataTO introduceObject(DataTO object, Scope scope, Long storeId) {
EndPoint ep = selector.select(scope, storeId);
IntroduceObjectCmd cmd = new IntroduceObjectCmd(object);
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (answer == null || !answer.getResult()) {
String errMsg = answer == null ? null : answer.getDetails();
throw new CloudRuntimeException("Failed to introduce object, due to " + errMsg);
@@ -54,7 +87,14 @@ public class HypervisorHelperImpl implements HypervisorHelper {
public boolean forgetObject(DataTO object, Scope scope, Long storeId) {
EndPoint ep = selector.select(scope, storeId);
ForgetObjectCmd cmd = new ForgetObjectCmd(object);
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (answer == null || !answer.getResult()) {
String errMsg = answer == null ? null : answer.getDetails();
if (errMsg != null) {
@@ -66,12 +106,52 @@ public class HypervisorHelperImpl implements HypervisorHelper {
}
@Override
- public SnapshotObjectTO takeSnapshot(SnapshotObjectTO snapshotObjectTO, Scope scope) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ public VMSnapshotTO quiesceVm(VirtualMachine virtualMachine) {
+ String value = configurationDao.getValue("vmsnapshot.create.wait");
+ int wait = NumbersUtil.parseInt(value, 1800);
+ Long hostId = vmSnapshotHelper.pickRunningHost(virtualMachine.getId());
+ VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(1L, UUID.randomUUID().toString(), VMSnapshot.Type.DiskAndMemory, null, null, false,
+ null);
+ GuestOSVO guestOS = guestOSDao.findById(virtualMachine.getGuestOSId());
+ List volumeTOs = vmSnapshotHelper.getVolumeTOList(virtualMachine.getId());
+ CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand(virtualMachine.getInstanceName(),vmSnapshotTO ,volumeTOs, guestOS.getDisplayName(),virtualMachine.getState());
+ ccmd.setWait(wait);
+ try {
+ Answer answer = agentMgr.send(hostId, ccmd);
+ if (answer != null && answer.getResult()) {
+ CreateVMSnapshotAnswer snapshotAnswer = (CreateVMSnapshotAnswer)answer;
+ vmSnapshotTO.setVolumes(snapshotAnswer.getVolumeTOs());
+ } else {
+ String errMsg = (answer != null) ? answer.getDetails() : null;
+ throw new CloudRuntimeException("Failed to quiesce vm, due to " + errMsg);
+ }
+ } catch (AgentUnavailableException e) {
+ throw new CloudRuntimeException("Failed to quiesce vm", e);
+ } catch (OperationTimedoutException e) {
+ throw new CloudRuntimeException("Failed to quiesce vm", e);
+ }
+ return vmSnapshotTO;
}
@Override
- public boolean revertSnapshot(SnapshotObjectTO snapshotObjectTO, Scope scope) {
- return false; //To change body of implemented methods use File | Settings | File Templates.
+ public boolean unquiesceVM(VirtualMachine virtualMachine, VMSnapshotTO vmSnapshotTO) {
+ Long hostId = vmSnapshotHelper.pickRunningHost(virtualMachine.getId());
+ List volumeTOs = vmSnapshotHelper.getVolumeTOList(virtualMachine.getId());
+ GuestOSVO guestOS = guestOSDao.findById(virtualMachine.getGuestOSId());
+
+ DeleteVMSnapshotCommand deleteSnapshotCommand = new DeleteVMSnapshotCommand(virtualMachine.getInstanceName(), vmSnapshotTO, volumeTOs, guestOS.getDisplayName());
+ try {
+ Answer answer = agentMgr.send(hostId, deleteSnapshotCommand);
+ if (answer != null && answer.getResult()) {
+ return true;
+ } else {
+ String errMsg = (answer != null) ? answer.getDetails() : null;
+ throw new CloudRuntimeException("Failed to unquiesce vm, due to " + errMsg);
+ }
+ } catch (AgentUnavailableException e) {
+ throw new CloudRuntimeException("Failed to unquiesce vm", e);
+ } catch (OperationTimedoutException e) {
+ throw new CloudRuntimeException("Failed to unquiesce vm", e);
+ }
}
}
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java b/engine/storage/src/org/apache/cloudstack/storage/helper/VMSnapshotHelperImpl.java
similarity index 97%
rename from engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java
rename to engine/storage/src/org/apache/cloudstack/storage/helper/VMSnapshotHelperImpl.java
index 320a59ce207..1200be89629 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/helper/VMSnapshotHelperImpl.java
@@ -16,16 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.cloudstack.storage.vmsnapshot;
+package org.apache.cloudstack.storage.helper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.cloudstack.storage.vmsnapshot.VMSnapshotHelper;
import com.cloud.agent.api.VMSnapshotTO;
-import com.cloud.agent.api.to.DataTO;
-import com.cloud.agent.api.to.VolumeTO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
-import com.cloud.storage.StoragePool;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.fsm.NoTransitionException;
@@ -36,17 +47,6 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
-import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
-import org.apache.cloudstack.storage.to.VolumeObjectTO;
-
-import javax.inject.Inject;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
public class VMSnapshotHelperImpl implements VMSnapshotHelper {
@Inject
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
index 3a70d8fcc55..bd93e73e0ef 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java
@@ -18,17 +18,13 @@
*/
package org.apache.cloudstack.storage.image;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.storage.DownloadAnswer;
-import com.cloud.agent.api.storage.Proxy;
-import com.cloud.agent.api.to.DataObjectType;
-import com.cloud.agent.api.to.DataTO;
-import com.cloud.storage.VMTemplateStorageResourceAssoc;
-import com.cloud.storage.VMTemplateVO;
-import com.cloud.storage.VolumeVO;
-import com.cloud.storage.dao.VMTemplateDao;
-import com.cloud.storage.dao.VolumeDao;
-import com.cloud.storage.download.DownloadMonitor;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Date;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
@@ -47,13 +43,17 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
-import org.apache.log4j.Logger;
-
-import javax.inject.Inject;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Date;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.storage.DownloadAnswer;
+import com.cloud.agent.api.storage.Proxy;
+import com.cloud.agent.api.to.DataObjectType;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.storage.VMTemplateStorageResourceAssoc;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.download.DownloadMonitor;
public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
private static final Logger s_logger = Logger.getLogger(BaseImageStoreDriverImpl.class);
@@ -239,7 +239,14 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
try {
DeleteCommand cmd = new DeleteCommand(data.getTO());
EndPoint ep = _epSelector.select(data);
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (answer != null && !answer.getResult()) {
result.setResult(answer.getDetails());
}
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java b/engine/storage/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java
similarity index 97%
rename from engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java
rename to engine/storage/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java
index 1437f800c21..9ee2f85dd3e 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java
@@ -18,14 +18,14 @@
*/
package org.apache.cloudstack.storage.vmsnapshot;
+import java.util.List;
+
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+
import com.cloud.agent.api.VMSnapshotTO;
-import com.cloud.agent.api.to.DataTO;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotVO;
-import org.apache.cloudstack.storage.to.VolumeObjectTO;
-
-import java.util.List;
public interface VMSnapshotHelper {
boolean vmSnapshotStateTransitTo(VMSnapshot vsnp, VMSnapshot.Event event) throws NoTransitionException;
diff --git a/engine/storage/src/org/apache/cloudstack/storage/volume/TemplateOnPrimaryDataStoreInfo.java b/engine/storage/src/org/apache/cloudstack/storage/volume/TemplateOnPrimaryDataStoreInfo.java
index b8d0857d495..5026f3e145f 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/volume/TemplateOnPrimaryDataStoreInfo.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/volume/TemplateOnPrimaryDataStoreInfo.java
@@ -19,7 +19,7 @@
package org.apache.cloudstack.storage.volume;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
-import org.apache.cloudstack.storage.datastore.PrimaryDataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
public interface TemplateOnPrimaryDataStoreInfo {
public String getPath();
diff --git a/engine/storage/storage.ucls b/engine/storage/storage.ucls
index 23a7b21fe00..4de955db04d 100644
--- a/engine/storage/storage.ucls
+++ b/engine/storage/storage.ucls
@@ -103,7 +103,7 @@
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
index 2d99c9b6b48..cb502e9e260 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java
@@ -23,23 +23,9 @@ import java.util.List;
import javax.inject.Inject;
+import org.apache.cloudstack.engine.subsystem.api.storage.*;
import org.apache.log4j.Logger;
-import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
-import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
-import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
-import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
-import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
-import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
-import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
-import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
-import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/PrimaryDataStoreProviderManagerImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/PrimaryDataStoreProviderManagerImpl.java
index db14f6d27a3..81919ec7689 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/PrimaryDataStoreProviderManagerImpl.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/PrimaryDataStoreProviderManagerImpl.java
@@ -29,7 +29,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManag
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.storage.datastore.PrimaryDataStoreImpl;
-import org.apache.cloudstack.storage.datastore.PrimaryDataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
index 870363afb24..5818aa8d924 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
@@ -18,6 +18,49 @@
*/
package org.apache.cloudstack.storage.volume;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity;
+import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
+import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
+import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
+import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
+import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
+import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
+import org.apache.cloudstack.framework.async.AsyncCallFuture;
+import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.framework.async.AsyncRpcContext;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.storage.command.CommandResult;
+import org.apache.cloudstack.storage.command.CopyCmdAnswer;
+import org.apache.cloudstack.storage.command.DeleteCommand;
+import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
+import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.storage.ListVolumeAnswer;
import com.cloud.agent.api.storage.ListVolumeCommand;
@@ -49,46 +92,6 @@ import com.cloud.utils.NumbersUtil;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.exception.CloudRuntimeException;
-import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity;
-import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
-import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
-import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
-import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
-import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
-import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
-import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
-import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
-import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
-import org.apache.cloudstack.framework.async.AsyncCallFuture;
-import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
-import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
-import org.apache.cloudstack.framework.async.AsyncRpcContext;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
-import org.apache.cloudstack.storage.command.CommandResult;
-import org.apache.cloudstack.storage.command.CopyCmdAnswer;
-import org.apache.cloudstack.storage.command.DeleteCommand;
-import org.apache.cloudstack.storage.datastore.PrimaryDataStore;
-import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager;
-import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
-import org.apache.cloudstack.storage.to.VolumeObjectTO;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
-
-import javax.inject.Inject;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
@Component
public class VolumeServiceImpl implements VolumeService {
@@ -162,12 +165,24 @@ public class VolumeServiceImpl implements VolumeService {
DataObject volumeOnStore = dataStore.create(volume);
volumeOnStore.processEvent(Event.CreateOnlyRequested);
- CreateVolumeContext context = new CreateVolumeContext(null, volumeOnStore,
- future);
- AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
- caller.setCallback(caller.getTarget().createVolumeCallback(null, null)).setContext(context);
+ try {
+ CreateVolumeContext context = new CreateVolumeContext(null, volumeOnStore,
+ future);
+ AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
+ caller.setCallback(caller.getTarget().createVolumeCallback(null, null)).setContext(context);
- dataStore.getDriver().createAsync(dataStore, volumeOnStore, caller);
+ dataStore.getDriver().createAsync(dataStore, volumeOnStore, caller);
+ } catch (CloudRuntimeException ex) {
+ // clean up already persisted volume_store_ref entry in case of createVolumeCallback is never called
+ VolumeDataStoreVO volStoreVO = _volumeStoreDao.findByStoreVolume(dataStore.getId(), volume.getId());
+ if (volStoreVO != null) {
+ VolumeInfo volObj = volFactory.getVolume(volume, dataStore);
+ volObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
+ }
+ VolumeApiResult volResult = new VolumeApiResult((VolumeObject)volumeOnStore);
+ volResult.setResult(ex.getMessage());
+ future.complete(volResult);
+ }
return future;
}
@@ -438,6 +453,7 @@ public class VolumeServiceImpl implements VolumeService {
} catch (Throwable e) {
s_logger.debug("failed to create template on storage", e);
templateOnPrimaryStoreObj.processEvent(Event.OperationFailed);
+ dataStore.create(template); // make sure that template_spool_ref entry is still present so that the second thread can acquire the lock
VolumeApiResult result = new VolumeApiResult(volume);
result.setResult(e.toString());
future.complete(result);
@@ -1019,13 +1035,25 @@ public class VolumeServiceImpl implements VolumeService {
volumeOnStore.processEvent(Event.CreateOnlyRequested);
- CreateVolumeContext context = new CreateVolumeContext(null, volumeOnStore,
- future);
- AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
- caller.setCallback(caller.getTarget().registerVolumeCallback(null, null));
- caller.setContext(context);
+ try {
+ CreateVolumeContext context = new CreateVolumeContext(null, volumeOnStore,
+ future);
+ AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this);
+ caller.setCallback(caller.getTarget().registerVolumeCallback(null, null));
+ caller.setContext(context);
- store.getDriver().createAsync(store, volumeOnStore, caller);
+ store.getDriver().createAsync(store, volumeOnStore, caller);
+ } catch (CloudRuntimeException ex) {
+ // clean up already persisted volume_store_ref entry in case of createVolumeCallback is never called
+ VolumeDataStoreVO volStoreVO = _volumeStoreDao.findByStoreVolume(store.getId(), volume.getId());
+ if (volStoreVO != null) {
+ VolumeInfo volObj = volFactory.getVolume(volume, store);
+ volObj.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
+ }
+ VolumeApiResult res = new VolumeApiResult((VolumeObject)volumeOnStore);
+ res.setResult(ex.getMessage());
+ future.complete(res);
+ }
return future;
}
@@ -1255,7 +1283,14 @@ public class VolumeServiceImpl implements VolumeService {
tmplTO.setId(tInfo.getId());
DeleteCommand dtCommand = new DeleteCommand(tmplTO);
EndPoint ep = _epSelector.select(store);
- Answer answer = ep.sendMessage(dtCommand);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(dtCommand, false, errMsg);
+ } else {
+ answer = ep.sendMessage(dtCommand);
+ }
if (answer == null || !answer.getResult()) {
s_logger.info("Failed to deleted volume at store: " + store.getName());
@@ -1280,7 +1315,14 @@ public class VolumeServiceImpl implements VolumeService {
private Map listVolume(DataStore store) {
ListVolumeCommand cmd = new ListVolumeCommand(store.getTO(), store.getUri());
EndPoint ep = _epSelector.select(store);
- Answer answer = ep.sendMessage(cmd);
+ Answer answer = null;
+ if (ep == null) {
+ String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
+ s_logger.error(errMsg);
+ answer = new Answer(cmd, false, errMsg);
+ } else {
+ answer = ep.sendMessage(cmd);
+ }
if (answer != null && answer.getResult()) {
ListVolumeAnswer tanswer = (ListVolumeAnswer) answer;
return tanswer.getTemplateInfo();
diff --git a/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java b/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java
index 71bea4f2e4f..1c10a95c328 100644
--- a/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java
+++ b/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java
@@ -59,6 +59,7 @@ import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.ConnectionConcierge;
import com.cloud.utils.db.DB;
+import com.cloud.utils.db.DbProperties;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionLegacy;
@@ -1029,15 +1030,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
s_logger.info("Start configuring cluster manager : " + name);
}
- File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
- Properties dbProps = new Properties();
- try {
- PropertiesUtil.loadFromFile(dbProps, dbPropsFile);
- } catch (FileNotFoundException e) {
- throw new ConfigurationException("Unable to find db.properties");
- } catch (IOException e) {
- throw new ConfigurationException("Unable to load db.properties content");
- }
+ Properties dbProps = DbProperties.getDbProperties();
_clusterNodeIP = dbProps.getProperty("cluster.node.IP");
if (_clusterNodeIP == null) {
_clusterNodeIP = "127.0.0.1";
diff --git a/framework/cluster/src/com/cloud/cluster/ClusterServiceServletAdapter.java b/framework/cluster/src/com/cloud/cluster/ClusterServiceServletAdapter.java
index f80e21f1d34..ebbbe984c5b 100644
--- a/framework/cluster/src/com/cloud/cluster/ClusterServiceServletAdapter.java
+++ b/framework/cluster/src/com/cloud/cluster/ClusterServiceServletAdapter.java
@@ -16,9 +16,6 @@
// under the License.
package com.cloud.cluster;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.Properties;
@@ -26,14 +23,13 @@ import java.util.Properties;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
-import org.apache.log4j.Logger;
-
import org.apache.cloudstack.framework.config.ConfigDepot;
+import org.apache.log4j.Logger;
import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.utils.NumbersUtil;
-import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.db.DbProperties;
public class ClusterServiceServletAdapter extends AdapterBase implements ClusterServiceAdapter {
@@ -122,15 +118,7 @@ public class ClusterServiceServletAdapter extends AdapterBase implements Cluster
if (_mshostDao != null)
return;
- File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
- Properties dbProps = new Properties();
- try {
- PropertiesUtil.loadFromFile(dbProps, dbPropsFile);
- } catch (FileNotFoundException e) {
- throw new ConfigurationException("Unable to find db.properties");
- } catch (IOException e) {
- throw new ConfigurationException("Unable to load db.properties content");
- }
+ Properties dbProps = DbProperties.getDbProperties();
_clusterServicePort = NumbersUtil.parseInt(dbProps.getProperty("cluster.servlet.port"), DEFAULT_SERVICE_PORT);
if (s_logger.isInfoEnabled())
diff --git a/framework/db/src/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/com/cloud/utils/db/GenericDaoBase.java
index ba5200ea65f..177cd5b6401 100755
--- a/framework/db/src/com/cloud/utils/db/GenericDaoBase.java
+++ b/framework/db/src/com/cloud/utils/db/GenericDaoBase.java
@@ -1864,4 +1864,11 @@ public abstract class GenericDaoBase extends Compone
return sql;
}
+
+ @DB()
+ protected Pair, Integer> listAndCountIncludingRemovedBy(final SearchCriteria sc, final Filter filter) {
+ List objects = searchIncludingRemoved(sc, filter, null, false);
+ Integer count = getCount(sc);
+ return new Pair, Integer>(objects, count);
+ }
}
diff --git a/framework/db/src/com/cloud/utils/db/TransactionLegacy.java b/framework/db/src/com/cloud/utils/db/TransactionLegacy.java
index a318d83f92b..a874ce10a56 100755
--- a/framework/db/src/com/cloud/utils/db/TransactionLegacy.java
+++ b/framework/db/src/com/cloud/utils/db/TransactionLegacy.java
@@ -93,12 +93,6 @@ public class TransactionLegacy {
} catch (Exception e) {
s_logger.error("Unable to register mbean for transaction", e);
}
-
- /* FIXME: We need a better solution for this
- * Initialize encryption if we need it for db.properties
- */
- EncryptionSecretKeyChecker enc = new EncryptionSecretKeyChecker();
- enc.check();
}
private final LinkedList _stack;
@@ -1025,30 +1019,23 @@ public class TransactionLegacy {
static {
// Initialize with assumed db.properties file
- initDataSource("db.properties");
+ initDataSource(DbProperties.getDbProperties());
}
- public static void initDataSource(String propsFileName) {
- try {
- File dbPropsFile = PropertiesUtil.findConfigFile(propsFileName);
- final Properties dbProps;
- if (EncryptionSecretKeyChecker.useEncryption()) {
- StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor();
- dbProps = new EncryptableProperties(encryptor);
- } else {
- dbProps = new Properties();
- }
- try {
- PropertiesUtil.loadFromFile(dbProps, dbPropsFile);
- dbProps.load(new FileInputStream(dbPropsFile));
- } catch (IOException e) {
- s_logger.fatal("Unable to load db properties file, pl. check the classpath and file path configuration", e);
- return;
- } catch (NullPointerException e) {
- s_logger.fatal("Unable to locate db properties file within classpath or absolute path: " + propsFileName);
- return;
- }
+ public static void initDataSource(String propsFileName) throws IOException {
+ Properties dbProps = new Properties();
+ File dbPropsFile = PropertiesUtil.findConfigFile(propsFileName);
+ if (dbPropsFile != null && dbPropsFile.exists()) {
+ PropertiesUtil.loadFromFile(dbProps, dbPropsFile);
+ initDataSource(dbProps);
+ }
+ }
+ public static void initDataSource(Properties dbProps) {
+ try {
+ if (dbProps.size() == 0)
+ return;
+
// FIXME: If params are missing...default them????
final int cloudMaxActive = Integer.parseInt(dbProps.getProperty("db.cloud.maxActive"));
final int cloudMaxIdle = Integer.parseInt(dbProps.getProperty("db.cloud.maxIdle"));
diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
index 29a299ff423..d79575273cd 100644
--- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
+++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java
@@ -17,7 +17,6 @@
package org.apache.cloudstack.framework.jobs.impl;
-import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
@@ -34,7 +33,6 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
-import org.apache.log4j.Logger;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.framework.config.ConfigDepot;
import org.apache.cloudstack.framework.config.ConfigKey;
@@ -54,25 +52,26 @@ import org.apache.cloudstack.jobs.JobInfo;
import org.apache.cloudstack.jobs.JobInfo.Status;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
+import org.apache.log4j.Logger;
import com.cloud.cluster.ClusterManagerListener;
import com.cloud.cluster.ManagementServerHost;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Predicate;
-import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
+import com.cloud.utils.db.DbProperties;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.db.TransactionCallback;
-import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.exception.ExceptionUtil;
@@ -865,10 +864,7 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager,
@Override
public boolean configure(String name, Map params) throws ConfigurationException {
try {
- final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
- final Properties dbProps = new Properties();
- PropertiesUtil.loadFromFile(dbProps, dbPropsFile);
-
+ final Properties dbProps = DbProperties.getDbProperties();
final int cloudMaxActive = Integer.parseInt(dbProps.getProperty("db.cloud.maxActive"));
int poolSize = (cloudMaxActive * 2) / 3;
diff --git a/parents/checkstyle/pom.xml b/parents/checkstyle/pom.xml
new file mode 100644
index 00000000000..48d58bb51d3
--- /dev/null
+++ b/parents/checkstyle/pom.xml
@@ -0,0 +1,21 @@
+
+
+ 4.0.0
+ Apache CloudStack Checkstyle Configuration
+ checkstyle
+
+ org.apache.cloudstack
+ cloud-maven-standard
+ 4.3.0-SNAPSHOT
+ ../../maven-standard/pom.xml
+
+
diff --git a/parents/checkstyle/src/main/resources/tooling/checkstyle.xml b/parents/checkstyle/src/main/resources/tooling/checkstyle.xml
new file mode 100644
index 00000000000..83493d677ba
--- /dev/null
+++ b/parents/checkstyle/src/main/resources/tooling/checkstyle.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore b/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore
new file mode 100644
index 00000000000..99afc0b89f2
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore
@@ -0,0 +1,9 @@
+packages
+*.suo
+*/obj/*
+WmiWrappers/bin/*
+AgentShell/bin/*
+ServerResource*/bin/*
+*.user
+!.nuget/
+
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/.nuget/NuGet.Config b/plugins/hypervisors/hyperv/DotNet/ServerResource/.nuget/NuGet.Config
new file mode 100644
index 00000000000..5181cb81034
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/.nuget/NuGet.Config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/.nuget/NuGet.targets b/plugins/hypervisors/hyperv/DotNet/ServerResource/.nuget/NuGet.targets
new file mode 100644
index 00000000000..d0ebc7535f3
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/.nuget/NuGet.targets
@@ -0,0 +1,136 @@
+
+
+
+ $(MSBuildProjectDirectory)\..\
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
+ $([System.IO.Path]::Combine($(ProjectDir), "packages.config"))
+
+
+
+
+ $(SolutionDir).nuget
+ packages.config
+
+
+
+
+ $(NuGetToolsPath)\NuGet.exe
+ @(PackageSource)
+
+ "$(NuGetExePath)"
+ mono --runtime=v4.0.30319 $(NuGetExePath)
+
+ $(TargetDir.Trim('\\'))
+
+ -RequireConsent
+ -NonInteractive
+
+ "$(SolutionDir) "
+ "$(SolutionDir)"
+
+
+ $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
+ $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
+
+
+
+ RestorePackages;
+ $(BuildDependsOn);
+
+
+
+
+ $(BuildDependsOn);
+ BuildPackage;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentService.Designer.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentService.Designer.cs
new file mode 100644
index 00000000000..854427732aa
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentService.Designer.cs
@@ -0,0 +1,53 @@
+// 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.
+namespace CloudStack.Plugin.AgentShell
+{
+ partial class AgentService
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ this.ServiceName = "CloudStack ServerResource";
+ }
+
+ #endregion
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentService.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentService.cs
new file mode 100644
index 00000000000..e1870973511
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentService.cs
@@ -0,0 +1,132 @@
+// 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.
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web.Http.SelfHost;
+using System.Web.Http;
+using log4net;
+using HypervResource;
+
+namespace CloudStack.Plugin.AgentShell
+{
+ public partial class AgentService : ServiceBase
+ {
+ [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool AllocConsole();
+
+ [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool FreeConsole();
+
+ HttpSelfHostServer server;
+
+ private static ILog logger = LogManager.GetLogger(typeof(AgentService));
+
+ public AgentService()
+ {
+ logger.Info("Starting CloudStack agent");
+ InitializeComponent();
+
+ UriBuilder baseUri = new UriBuilder("http", AgentSettings.Default.private_ip_address, AgentSettings.Default.port);
+
+ var config = new HttpSelfHostConfiguration(baseUri.Uri);
+
+ // Allow ActionName to be applied to methods in ApiController, which allows it to serve multiple POST URLs
+ config.Routes.MapHttpRoute(
+ "API Default", "api/{controller}/{action}",
+ new { action = RouteParameter.Optional }
+ );
+
+ // Load controller assemblies that we want to config to route to.
+ ConfigServerResource();
+ AssertControllerAssemblyAvailable(config, typeof(HypervResourceController), "Cannot load Controller of type" + typeof(HypervResourceController));
+
+ server = new HttpSelfHostServer(config);
+ }
+
+ public static void ConfigServerResource()
+ {
+ // For simplicity, ServerResource config and settings file are tightly coupled.
+ // An alternative is to pass a dictionary to the server resource and let it find
+ // required settings. In contrast, the approach below is strongly typed and makes
+ // use of VisualStudio settings designer. The designer allows us to avoid
+ // accessing config using their key strings.
+ HypervResourceControllerConfig rsrcCnf = new HypervResourceControllerConfig();
+ rsrcCnf.PrivateIpAddress = AgentSettings.Default.private_ip_address;
+ rsrcCnf.GatewayIpAddress = AgentSettings.Default.gateway_ip_address;
+ rsrcCnf.RootDeviceReservedSpaceBytes = AgentSettings.Default.RootDeviceReservedSpaceBytes;
+ rsrcCnf.RootDeviceName = AgentSettings.Default.RootDeviceName;
+ rsrcCnf.ParentPartitionMinMemoryMb = AgentSettings.Default.dom0MinMemory;
+ rsrcCnf.LocalSecondaryStoragePath = AgentSettings.Default.local_secondary_storage_path;
+ rsrcCnf.systemVmIso = null;
+
+ // Side effect: loads the assembly containing HypervResourceController, which
+ // allows HttpSelfHostServer to route requests to the controller.
+ HypervResourceController.Configure(rsrcCnf);
+
+ }
+
+ // TODO: update to examine not the assembly resolver, but the list of available controllers themselves!
+ private static bool AssertControllerAssemblyAvailable(HttpSelfHostConfiguration config, Type controllerType, string errorMessage)
+ {
+ var assemblies = config.Services.GetAssembliesResolver().GetAssemblies();
+ foreach (var assembly in assemblies)
+ {
+ string name = assembly.GetName().Name;
+ if (controllerType.Assembly.GetName().Name.Equals(name))
+ {
+ logger.DebugFormat("Controller {0} is available", controllerType.Name);
+ return true;
+ }
+ }
+
+ logger.Error(errorMessage);
+ throw new AgentShellException(errorMessage);
+ }
+
+ protected override void OnStart(string[] args)
+ {
+ server.OpenAsync().Wait();
+ }
+
+ protected override void OnStop()
+ {
+ server.CloseAsync().Wait();
+ }
+
+ internal void RunConsole(string[] args)
+ {
+ OnStart(args);
+
+ AllocConsole();
+
+ Console.WriteLine("Service running ... press to stop");
+
+ Console.ReadLine();
+
+ FreeConsole();
+
+ OnStop();
+ }
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs
new file mode 100644
index 00000000000..fc3b69af3e8
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs
@@ -0,0 +1,402 @@
+// 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.
+
+namespace CloudStack.Plugin.AgentShell {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ public sealed partial class AgentSettings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static AgentSettings defaultInstance = ((AgentSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AgentSettings())));
+
+ public static AgentSettings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("8")]
+ public int cpus {
+ get {
+ return ((int)(this["cpus"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("Routing")]
+ public string type {
+ get {
+ return ((string)(this["type"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("5fe2bad3-d785-394e-9949-89786b8a63d2")]
+ public global::System.Guid local_storage_uuid {
+ get {
+ return ((global::System.Guid)(this["local_storage_uuid"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("hvm")]
+ public string capabilities {
+ get {
+ return ((string)(this["capabilities"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("2130")]
+ public int Settingcpuspeed {
+ get {
+ return ((int)(this["Settingcpuspeed"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("8250")]
+ public int port {
+ get {
+ return ((int)(this["port"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("34359738368")]
+ public long memory {
+ get {
+ return ((long)(this["memory"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("camldonall01.citrite.net")]
+ public string host {
+ get {
+ return ((string)(this["host"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("1")]
+ public int pod {
+ get {
+ return ((int)(this["pod"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("10.1.1.1")]
+ public string gateway_ip_address {
+ get {
+ return ((string)(this["gateway_ip_address"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public global::System.Guid guid {
+ get {
+ return ((global::System.Guid)(this["guid"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("2")]
+ public int cluster {
+ get {
+ return ((int)(this["cluster"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("1")]
+ public int zone {
+ get {
+ return ((int)(this["zone"]));
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("c:\\Secondary")]
+ public string local_secondary_storage_path {
+ get {
+ return ((string)(this["local_secondary_storage_path"]));
+ }
+ set {
+ this["local_secondary_storage_path"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("E:\\Disks\\Disks")]
+ public string local_storage_path {
+ get {
+ return ((string)(this["local_storage_path"]));
+ }
+ set {
+ this["local_storage_path"] = value;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("5")]
+ public int workers {
+ get {
+ return ((int)(this["workers"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("255.255.240.0")]
+ public string private_ip_netmask {
+ get {
+ return ((string)(this["private_ip_netmask"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("4294967296")]
+ public long RootDeviceReservedSpaceBytes {
+ get {
+ return ((long)(this["RootDeviceReservedSpaceBytes"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("../../../../../")]
+ public string hyperv_plugin_root {
+ get {
+ return ((string)(this["hyperv_plugin_root"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("e:\\")]
+ public string RootDeviceName {
+ get {
+ return ((string)(this["RootDeviceName"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("101F742C6B88")]
+ public string private_mac_address {
+ get {
+ return ((string)(this["private_mac_address"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("systemvm.iso")]
+ public string system_vm_iso {
+ get {
+ return ((string)(this["system_vm_iso"]));
+ }
+ set {
+ this["system_vm_iso"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute(".\\var\\test\\storagepool")]
+ public string testLocalStorePath {
+ get {
+ return ((string)(this["testLocalStorePath"]));
+ }
+ set {
+ this["testLocalStorePath"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("testS3Bucket")]
+ public string testS3Bucket {
+ get {
+ return ((string)(this["testS3Bucket"]));
+ }
+ set {
+ this["testS3Bucket"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("testS3Endpoint")]
+ public string testS3Endpoint {
+ get {
+ return ((string)(this["testS3Endpoint"]));
+ }
+ set {
+ this["testS3Endpoint"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("testS3AccessKey")]
+ public string testS3AccessKey {
+ get {
+ return ((string)(this["testS3AccessKey"]));
+ }
+ set {
+ this["testS3AccessKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("testS3SecretKey")]
+ public string testS3SecretKey {
+ get {
+ return ((string)(this["testS3SecretKey"]));
+ }
+ set {
+ this["testS3SecretKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("206-2-73592258-559a-3b38-8f66-b667aab143eb")]
+ public string testS3TemplateName {
+ get {
+ return ((string)(this["testS3TemplateName"]));
+ }
+ set {
+ this["testS3TemplateName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("5fe2bad3-d785-394e-9949-89786b8a63d2")]
+ public string testLocalStoreUUID {
+ get {
+ return ((string)(this["testLocalStoreUUID"]));
+ }
+ set {
+ this["testLocalStoreUUID"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("acton-systemvm-02062012.vhd.bz2")]
+ public string testSystemVMTemplateName {
+ get {
+ return ((string)(this["testSystemVMTemplateName"]));
+ }
+ set {
+ this["testSystemVMTemplateName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("acton-systemvm-02062012")]
+ public string testSystemVMTemplateNameNoExt {
+ get {
+ return ((string)(this["testSystemVMTemplateNameNoExt"]));
+ }
+ set {
+ this["testSystemVMTemplateNameNoExt"] = value;
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("2048")]
+ public ulong dom0MinMemory {
+ get {
+ return ((ulong)(this["dom0MinMemory"]));
+ }
+ }
+
+ [global::System.Configuration.ApplicationScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("10.1.1.1")]
+ public string private_ip_address {
+ get {
+ return ((string)(this["private_ip_address"]));
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("cifs://10.1.1.1/secondary?user\\u003dadministrator\\u0026password\\u003d1pass%40w" +
+ "ord1")]
+ public string testCifsUrl {
+ get {
+ return ((string)(this["testCifsUrl"]));
+ }
+ set {
+ this["testCifsUrl"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("template/tmpl/2/201/61d5316a-3cc4-30cf-a557-78691ff5c143.vhd")]
+ public string testCifsPath {
+ get {
+ return ((string)(this["testCifsPath"]));
+ }
+ set {
+ this["testCifsPath"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("CentOS64")]
+ public string testKvpVmName {
+ get {
+ return ((string)(this["testKvpVmName"]));
+ }
+ set {
+ this["testKvpVmName"] = value;
+ }
+ }
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings
new file mode 100644
index 00000000000..fb5b962fcaa
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+ 8
+
+
+ 10.70.176.29
+
+
+ Routing
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ hvm
+
+
+ 2130
+
+
+ 8250
+
+
+ 34359738368
+
+
+ camldonall01.citrite.net
+
+
+ 1
+
+
+ 10.70.176.1
+
+
+
+
+
+ 2
+
+
+ 1
+
+
+ c:\Secondary
+
+
+ E:\Disks\Disks
+
+
+ 5
+
+
+ 255.255.240.0
+
+
+ 4294967296
+
+
+ ../../../../../
+
+
+ e:\
+
+
+ 101F742C6B88
+
+
+ .\var\test\storagepool
+
+
+ testS3Bucket
+
+
+ testS3Endpoint
+
+
+ testS3AccessKey
+
+
+ testS3SecretKey
+
+
+ 206-2-73592258-559a-3b38-8f66-b667aab143eb
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ acton-systemvm-02062012.vhd.bz2
+
+
+ acton-systemvm-02062012
+
+
+ 2048
+
+
+ cifs://10.1.1.1/secondary?user\u003dadministrator\u0026password\u003d1pass%40word1
+
+
+ template/tmpl/2/201/61d5316a-3cc4-30cf-a557-78691ff5c143.vhd
+
+
+ CentOS64
+
+
+
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentShell.csproj b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentShell.csproj
new file mode 100644
index 00000000000..39fef1606a2
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentShell.csproj
@@ -0,0 +1,140 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {9060B539-62D0-4E71-A6C6-5944828774E9}
+ WinExe
+ Properties
+ CloudStack.Plugin.AgentShell
+ AgentShell
+ v4.5
+ 512
+ ..\
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ bin\NoUnitTestsDebug\
+ DEBUG;TRACE
+ full
+ AnyCPU
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ bin\NoUnitTests\
+ TRACE
+ true
+ pdbonly
+ AnyCPU
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+
+ ..\packages\AWSSDK.1.5.23.0\lib\AWSSDK.dll
+
+
+ ..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll
+
+
+ ..\packages\log4net.2.0.0\lib\net40-full\log4net.dll
+
+
+ ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
+
+
+ ..\packages\NSubstitute.1.6.1.0\lib\NET40\NSubstitute.dll
+
+
+
+
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll
+
+
+ ..\packages\Microsoft.AspNet.WebApi.SelfHost.4.0.20918.0\lib\net40\System.Web.Http.SelfHost.dll
+
+
+
+
+
+
+
+
+ ..\packages\xunit.1.9.2\lib\net20\xunit.dll
+
+
+
+
+ Component
+
+
+ AgentService.cs
+
+
+
+
+
+ True
+ True
+ AgentSettings.settings
+
+
+
+
+ Designer
+
+
+ PublicSettingsSingleFileGenerator
+ AgentSettings.Designer.cs
+ Designer
+
+
+
+
+ {c963dfff-65ba-4e71-ada5-526a4da4e0b2}
+ HypervResource
+
+
+ {db824727-bdc3-437c-a364-7a811d8a160f}
+ WmiWrappers
+
+
+
+
+
+
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentShellException.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentShellException.cs
new file mode 100644
index 00000000000..14d9e9783b8
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentShellException.cs
@@ -0,0 +1,28 @@
+// 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.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace CloudStack.Plugin.AgentShell
+{
+ class AgentShellException : Exception
+ {
+ public AgentShellException(string errMsg) : base(errMsg) { }
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/App.config b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/App.config
new file mode 100644
index 00000000000..68ab80ee555
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/App.config
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ Routing
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ hvm
+
+
+ 2130
+
+
+ 8250
+
+
+ 34359738368
+
+
+ camldonall01.citrite.net
+
+
+ 1
+
+
+ 10.102.192.1
+
+
+ 2
+
+
+ 1
+
+
+ 5
+
+
+ 255.255.252.0
+
+
+ 4294967296
+
+
+ ..\..\..\..\..\
+
+
+ e:\
+
+
+ 101F742C6B88
+
+
+ 2048
+
+
+ 10.102.192.150
+
+
+
+
+
+
+ c:\Secondary
+
+
+ E:\Disks\Disks
+
+
+ .\var\test\storagepool
+
+
+ testS3Bucket
+
+
+ testS3Endpoint
+
+
+ testS3AccessKey
+
+
+ testS3SecretKey
+
+
+ 206-2-73592258-559a-3b38-8f66-b667aab143eb
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ acton-systemvm-02062012.vhd.bz2
+
+
+ acton-systemvm-02062012
+
+
+ cifs://10.70.1.1/secondary?user\u003dadministrator\u0026password\u003d1pass%40word1
+
+
+ template/tmpl/2/201/61d5316a-3cc4-30cf-a557-78691ff5c143.vhd
+
+
+
+
\ No newline at end of file
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs
new file mode 100644
index 00000000000..10663708508
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs
@@ -0,0 +1,58 @@
+// 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.
+using log4net;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CloudStack.Plugin.AgentShell
+{
+ static class Program
+ {
+ private static ILog logger = LogManager.GetLogger(typeof(Program));
+
+ ///
+ /// Application entry point allows service to run in console application or as a Windows service.
+ /// Add '--console' to the commandline for the former, the latter is the default.
+ ///
+ static void Main(params string[] args)
+ {
+ string arg1 = string.Empty;
+
+ if (args.Length > 0)
+ {
+ arg1 = args[0];
+ logger.DebugFormat("CloudStack ServerResource arg is ", arg1);
+ }
+
+ if (string.Compare(arg1, "--console", true) == 0)
+ {
+ logger.InfoFormat("CloudStack ServerResource running as console app");
+ new AgentService().RunConsole(args);
+ }
+ else
+ {
+ logger.InfoFormat("CloudStack ServerResource running as Windows Service");
+ ServiceBase[] ServicesToRun = new ServiceBase[] { new AgentService() };
+ ServiceBase.Run(ServicesToRun);
+ }
+ }
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Properties/AssemblyInfo.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000000..bd188bb0ef2
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Properties/AssemblyInfo.cs
@@ -0,0 +1,56 @@
+// 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.
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AgentShell")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AgentShell")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a6d8469a-c815-4765-a4a1-4927d4e3c583")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+
+
+[assembly: log4net.Config.XmlConfigurator(Watch = true)]
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/packages.config b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/packages.config
new file mode 100644
index 00000000000..fb1c846ad3c
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/packages.config
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config
new file mode 100644
index 00000000000..1bf17d4791f
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 10.1.1.1
+
+
+ Routing
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ hvm
+
+
+ 2130
+
+
+ 8250
+
+
+ 34359738368
+
+
+ camldonall01.citrite.net
+
+
+ 1
+
+
+ 10.70.176.1
+
+
+ 2
+
+
+ 1
+
+
+ E:\Disks\Disks
+
+
+ 5
+
+
+ 255.255.240.0
+
+
+ 4294967296
+
+
+ ..\..\..\..\..\
+
+
+ e:\
+
+
+ 2048
+
+
+ 101F742C6B88
+
+
+
+
+
+
+ c:\Secondary
+
+
+ E:\Disks\Disks
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ .\var\test\storagepool
+
+
+ cshv3eu
+
+
+ s3.amazonaws.com
+
+
+ testS3AccessKey
+
+
+ testS3SecretKey
+
+
+ 206-2-73592258-559a-3b38-8f66-b667aab143eb
+
+
+ 206-2-73592258-559a-3b38-8f66-b667aab143eb
+
+
+ cifs://10.1.1.1/secondary?user\u003dadministrator\u0026password\u003d1pass%40word1
+
+
+ template/tmpl/2/201/6dda6631-4daa-3150-a49a-d5a4b0a4c4b6.vhd
+
+
+
+
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
new file mode 100644
index 00000000000..ae595fcc215
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
@@ -0,0 +1,642 @@
+// 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.
+using log4net;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+// C# versions of certain CloudStack types to simplify JSON serialisation.
+// Limit to the number of types, becasue they are written and maintained manually.
+// JsonProperty used to identify property name when serialised, which allows
+// later adoption of C# naming conventions if requried.
+namespace HypervResource
+{
+ public class PrimaryDataStoreTO
+ {
+ public string path;
+
+ public static PrimaryDataStoreTO ParseJson(dynamic json)
+ {
+ PrimaryDataStoreTO result = null;
+
+ if (json == null)
+ {
+ return result;
+ }
+ dynamic primaryDataStoreTOJson = json[CloudStackTypes.PrimaryDataStoreTO];
+ if (primaryDataStoreTOJson != null)
+ {
+ result = new PrimaryDataStoreTO()
+ {
+ path = (string)primaryDataStoreTOJson.path
+ };
+ }
+ return result;
+ }
+ }
+
+ public class VolumeObjectTO
+ {
+ private static ILog logger = LogManager.GetLogger(typeof(VolumeObjectTO));
+
+ public string FullFileName
+ {
+ get
+ {
+ String result = Path.Combine(this.primaryDataStore.path, this.name);
+ if (this.format != null)
+ {
+ result = result + "." + this.format.ToLowerInvariant();
+ }
+ return result;
+ }
+ }
+
+ public dynamic dataStore;
+ public string format;
+ public string name;
+ public string uuid;
+ public PrimaryDataStoreTO primaryDataStore;
+
+ public static VolumeObjectTO ParseJson(dynamic json)
+ {
+ VolumeObjectTO result = null;
+
+ if (json == null)
+ {
+ return result;
+ }
+
+ dynamic volumeObjectTOJson = json[CloudStackTypes.VolumeObjectTO];
+ if (volumeObjectTOJson != null)
+ {
+ result = new VolumeObjectTO()
+ {
+ dataStore = volumeObjectTOJson.dataStore,
+ format = ((string)volumeObjectTOJson.format),
+ name = (string)volumeObjectTOJson.name,
+ uuid = (string)volumeObjectTOJson.uuid
+ };
+ result.primaryDataStore = PrimaryDataStoreTO.ParseJson(volumeObjectTOJson.dataStore);
+
+ // Assert
+ if (result.dataStore == null || result.primaryDataStore == null)
+ {
+ String errMsg = "VolumeObjectTO missing primary dataStore in spec " + volumeObjectTOJson.ToString();
+ logger.Error(errMsg);
+ throw new ArgumentNullException(errMsg);
+ }
+
+ GuessFileExtension(result);
+ }
+ return result;
+ }
+
+ private static void GuessFileExtension(VolumeObjectTO volInfo)
+ {
+ if (String.IsNullOrEmpty(volInfo.format))
+ {
+ logger.Info("No image format in VolumeObjectTO, going to use format from first file that matches " + volInfo.FullFileName);
+
+ string[] choices = Directory.GetFiles(volInfo.primaryDataStore.path, volInfo.name + ".vhd*");
+
+ if (choices.Length != 1)
+ {
+ String errMsg = "Tried to guess file extension, but cannot find file corresponding to " + Path.Combine(volInfo.primaryDataStore.path, volInfo.name); // format being guessed.
+ logger.Debug(errMsg);
+ }
+ else
+ {
+ string[] splitFileName = choices[0].Split(new char[] { '.' });
+ volInfo.format = splitFileName[splitFileName.Length - 1];
+ }
+ logger.Debug("Going to use file " + volInfo.FullFileName);
+ }
+ }
+ }
+
+ public class TemplateObjectTO
+ {
+ ///
+ /// Full file name varies depending on whether the TemplateObjectTO has a path or not.
+ /// If it has a path, we use that. Otherwise, we build it from the name, extension and data store path.
+ ///
+ public string FullFileName
+ {
+ get
+ {
+ if (String.IsNullOrEmpty(this.path))
+ {
+ return Path.Combine(this.primaryDataStore.path, this.name) + '.' + this.format.ToLowerInvariant();
+ }
+ return this.path;
+ }
+ }
+
+ public dynamic imageDataStore;
+ public string format;
+ public string name;
+ public string uuid;
+ public S3TO s3DataStoreTO = null;
+ public NFSTO nfsDataStoreTO = null;
+ public PrimaryDataStoreTO primaryDataStore = null;
+ public string path;
+ public string checksum;
+
+ public static TemplateObjectTO ParseJson(dynamic json)
+ {
+ TemplateObjectTO result = null;
+ dynamic templateObjectTOJson = json[CloudStackTypes.TemplateObjectTO];
+ if (templateObjectTOJson != null)
+ {
+ result = new TemplateObjectTO()
+ {
+ imageDataStore = templateObjectTOJson.imageDataStore,
+ format = (string)templateObjectTOJson.format,
+ name = (string)templateObjectTOJson.name,
+ uuid = (string)templateObjectTOJson.uuid,
+ path = (string)templateObjectTOJson.path,
+ checksum = (string)templateObjectTOJson.checksum
+ };
+ result.s3DataStoreTO = S3TO.ParseJson(templateObjectTOJson.imageDataStore);
+ result.nfsDataStoreTO = NFSTO.ParseJson(templateObjectTOJson.imageDataStore);
+ result.primaryDataStore = PrimaryDataStoreTO.ParseJson(templateObjectTOJson.imageDataStore);
+ }
+
+ return result;
+ }
+ }
+
+ public class S3TO
+ {
+ public string bucketName;
+ public string secretKey;
+ public string accessKey;
+ public string endpoint;
+ public bool httpsFlag;
+
+ public static S3TO ParseJson(dynamic json)
+ {
+ S3TO result = null;
+ dynamic s3TOJson = json[CloudStackTypes.S3TO];
+ if (s3TOJson != null)
+ {
+ result = new S3TO()
+ {
+ bucketName = (string)s3TOJson.bucketName,
+ secretKey = (string)s3TOJson.secretKey,
+ accessKey = (string)s3TOJson.accessKey,
+ endpoint = (string)s3TOJson.endPoint,
+ httpsFlag = (bool)s3TOJson.httpsFlag
+ };
+ // Delete security credentials in original command. Prevents logger from spilling the beans, as it were.
+ s3TOJson.secretKey = string.Empty;
+ s3TOJson.accessKey = string.Empty;
+ }
+ return result;
+ }
+ }
+
+ public class NFSTO
+ {
+ public Uri uri;
+ public string _role;
+ public string UncPath
+ {
+ get
+ {
+ string uncPath = null;
+ if (uri.Scheme.Equals("cifs"))
+ {
+ uncPath = @"\\" + uri.Host + uri.LocalPath;
+ }
+ return uncPath;
+ }
+ }
+ public string User
+ {
+ get
+ {
+ var queryDictionary = System.Web.HttpUtility.ParseQueryString(uri.Query);
+ return System.Web.HttpUtility.UrlDecode(queryDictionary["user"]);
+ }
+ }
+
+ public string Password
+ {
+ get
+ {
+ var queryDictionary = System.Web.HttpUtility.ParseQueryString(uri.Query);
+ return System.Web.HttpUtility.UrlDecode(queryDictionary["password"]);
+ }
+ }
+
+ public string Domain
+ {
+ get
+ {
+ var queryDictionary = System.Web.HttpUtility.ParseQueryString(uri.Query);
+ if (queryDictionary["domain"] != null)
+ {
+ return System.Web.HttpUtility.UrlDecode(queryDictionary["domain"]);
+ }
+ else return uri.Host;
+ }
+ }
+ public static NFSTO ParseJson(dynamic json)
+ {
+ NFSTO result = null;
+ dynamic nfsTOJson = json[CloudStackTypes.NFSTO];
+ if (nfsTOJson != null)
+ {
+ result = new NFSTO()
+ {
+ _role = (string)nfsTOJson._role,
+ };
+ // Delete security credentials in original command. Prevents logger from spilling the beans, as it were.
+ String uriStr = (String)nfsTOJson._url;
+ result.uri = new Uri(uriStr);
+ }
+ return result;
+ }
+ }
+
+ enum VolumeType
+ {
+ UNKNOWN,
+ ROOT,
+ SWAP,
+ DATADISK,
+ ISO
+ };
+
+ public enum StoragePoolType
+ {
+ ///
+ /// local directory
+ ///
+ Filesystem,
+ ///
+ /// NFS or CIFS
+ ///
+ NetworkFilesystem,
+ ///
+ /// shared LUN, with a clusterfs overlay
+ ///
+ IscsiLUN,
+ ///
+ /// for e.g., ZFS Comstar
+ ///
+ Iscsi,
+ ///
+ /// for iso image
+ ///
+ ISO,
+ ///
+ /// XenServer local LVM SR
+ ///
+ LVM,
+ ///
+ ///
+ ///
+ CLVM,
+ ///
+ ///
+ ///
+ RBD,
+ ///
+ ///
+ ///
+ SharedMountPoint,
+ ///
+ /// VMware VMFS storage
+ ///
+ VMFS,
+ ///
+ /// for XenServer, Storage Pool is set up by customers.
+ ///
+ PreSetup,
+ ///
+ /// XenServer local EXT SR
+ ///
+ EXT,
+ ///
+ ///
+ ///
+ OCFS2
+ }
+
+ public enum StorageResourceType
+ {
+ STORAGE_POOL, STORAGE_HOST, SECONDARY_STORAGE, LOCAL_SECONDARY_STORAGE
+ }
+
+ public struct VolumeInfo
+ {
+ public long id;
+ public string type;
+ public string storagePoolType;
+ public string storagePoolUuid;
+ public string name;
+ public string mountPoint;
+ public string path;
+ long size;
+ string chainInfo;
+
+ public VolumeInfo(long id, string type, string poolType, String poolUuid, String name, String mountPoint, String path, long size, String chainInfo)
+ {
+ this.id = id;
+ this.name = name;
+ this.path = path;
+ this.size = size;
+ this.type = type;
+ this.storagePoolType = poolType;
+ this.storagePoolUuid = poolUuid;
+ this.mountPoint = mountPoint;
+ this.chainInfo = chainInfo;
+ }
+ }
+
+ public struct StoragePoolInfo
+ {
+ [JsonProperty("uuid")]
+ public String uuid;
+ [JsonProperty("host")]
+ String host;
+ [JsonProperty("localPath")]
+ String localPath;
+ [JsonProperty("hostPath")]
+ String hostPath;
+ [JsonProperty("poolType")]
+ string poolType;
+ [JsonProperty("capacityBytes")]
+ long capacityBytes;
+
+ // Management server copies this field to the 'used_byptes' in the database table 'storage_pool'.
+ [JsonProperty("availableBytes")]
+ long availableBytes;
+ [JsonProperty("details")]
+ Dictionary details;
+
+ public StoragePoolInfo(String uuid, String host, String hostPath,
+ String localPath, string poolType, long capacityBytes,
+ long availableBytes)
+ {
+ this.uuid = uuid;
+ this.host = host;
+ this.localPath = localPath;
+ this.hostPath = hostPath;
+ this.poolType = poolType;
+ this.capacityBytes = capacityBytes;
+ this.availableBytes = availableBytes;
+ details = null;
+ }
+
+ public StoragePoolInfo(String uuid, String host, String hostPath,
+ String localPath, string poolType, long capacityBytes,
+ long availableBytes, Dictionary details)
+ : this(uuid, host, hostPath, localPath, poolType, capacityBytes, availableBytes)
+ {
+ this.details = details;
+ }
+ }
+
+ public class VmStatsEntry
+ {
+ [JsonProperty("cpuUtilization")]
+ public double cpuUtilization;
+ [JsonProperty("networkReadKBs")]
+ public double networkReadKBs;
+ [JsonProperty("networkWriteKBs")]
+ public double networkWriteKBs;
+ [JsonProperty("numCPUs")]
+ public int numCPUs;
+ [JsonProperty("entityType")]
+ public String entityType;
+ }
+
+ ///
+ /// Fully qualified named for a number of types used in CloudStack. Used to specify the intended type for JSON serialised objects.
+ ///
+ public class CloudStackTypes
+ {
+ public const string Answer = "com.cloud.agent.api.Answer";
+ public const string AttachIsoCommand = "com.cloud.agent.api.AttachIsoCommand";
+ public const string AttachVolumeAnswer = "com.cloud.agent.api.AttachVolumeAnswer";
+ public const string AttachVolumeCommand = "com.cloud.agent.api.AttachVolumeCommand";
+ public const string AnsBackupSnapshotAnswerwer = "com.cloud.agent.api.BackupSnapshotAnswer";
+ public const string BackupSnapshotCommand = "com.cloud.agent.api.BackupSnapshotCommand";
+ public const string BumpUpPriorityCommand = "com.cloud.agent.api.BumpUpPriorityCommand";
+ public const string CheckHealthAnswer = "com.cloud.agent.api.CheckHealthAnswer";
+ public const string CheckHealthCommand = "com.cloud.agent.api.CheckHealthCommand";
+ public const string CheckNetworkAnswer = "com.cloud.agent.api.CheckNetworkAnswer";
+ public const string CheckNetworkCommand = "com.cloud.agent.api.CheckNetworkCommand";
+ public const string CheckOnHostAnswer = "com.cloud.agent.api.CheckOnHostAnswer";
+ public const string CheckOnHostCommand = "com.cloud.agent.api.CheckOnHostCommand";
+ public const string CheckRouterAnswer = "com.cloud.agent.api.CheckRouterAnswer";
+ public const string CheckRouterCommand = "com.cloud.agent.api.CheckRouterCommand";
+ public const string CheckS2SVpnConnectionsAnswer = "com.cloud.agent.api.CheckS2SVpnConnectionsAnswer";
+ public const string CheckS2SVpnConnectionsCommand = "com.cloud.agent.api.CheckS2SVpnConnectionsCommand";
+ public const string CheckVirtualMachineAnswer = "com.cloud.agent.api.CheckVirtualMachineAnswer";
+ public const string CheckVirtualMachineCommand = "com.cloud.agent.api.CheckVirtualMachineCommand";
+ public const string CleanupNetworkRulesCmd = "com.cloud.agent.api.CleanupNetworkRulesCmd";
+ public const string ClusterSyncAnswer = "com.cloud.agent.api.ClusterSyncAnswer";
+ public const string ClusterSyncCommand = "com.cloud.agent.api.ClusterSyncCommand";
+ public const string Command = "com.cloud.agent.api.Command";
+ public const string CreatePrivateTemplateFromSnapshotCommand = "com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand";
+ public const string CreatePrivateTemplateFromVolumeCommand = "com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand";
+ public const string CreateStoragePoolCommand = "com.cloud.agent.api.CreateStoragePoolCommand";
+ public const string CreateVMSnapshotAnswer = "com.cloud.agent.api.CreateVMSnapshotAnswer";
+ public const string CreateVMSnapshotCommand = "com.cloud.agent.api.CreateVMSnapshotCommand";
+ public const string CreateVolumeFromSnapshotAnswer = "com.cloud.agent.api.CreateVolumeFromSnapshotAnswer";
+ public const string CreateVolumeFromSnapshotCommand = "com.cloud.agent.api.CreateVolumeFromSnapshotCommand";
+ public const string DeleteStoragePoolCommand = "com.cloud.agent.api.DeleteStoragePoolCommand";
+ public const string DeleteVMSnapshotAnswer = "com.cloud.agent.api.DeleteVMSnapshotAnswer";
+ public const string DeleteVMSnapshotCommand = "com.cloud.agent.api.DeleteVMSnapshotCommand";
+ public const string GetDomRVersionAnswer = "com.cloud.agent.api.GetDomRVersionAnswer";
+ public const string GetDomRVersionCmd = "com.cloud.agent.api.GetDomRVersionCmd";
+ public const string GetHostStatsAnswer = "com.cloud.agent.api.GetHostStatsAnswer";
+ public const string GetHostStatsCommand = "com.cloud.agent.api.GetHostStatsCommand";
+ public const string GetStorageStatsAnswer = "com.cloud.agent.api.GetStorageStatsAnswer";
+ public const string GetStorageStatsCommand = "com.cloud.agent.api.GetStorageStatsCommand";
+ public const string GetVmDiskStatsAnswer = "com.cloud.agent.api.GetVmDiskStatsAnswer";
+ public const string GetVmDiskStatsCommand = "com.cloud.agent.api.GetVmDiskStatsCommand";
+ public const string GetVmStatsAnswer = "com.cloud.agent.api.GetVmStatsAnswer";
+ public const string GetVmStatsCommand = "com.cloud.agent.api.GetVmStatsCommand";
+ public const string GetVncPortAnswer = "com.cloud.agent.api.GetVncPortAnswer";
+ public const string GetVncPortCommand = "com.cloud.agent.api.GetVncPortCommand";
+ public const string HostStatsEntry = "com.cloud.agent.api.HostStatsEntry";
+ public const string MaintainAnswer = "com.cloud.agent.api.MaintainAnswer";
+ public const string MaintainCommand = "com.cloud.agent.api.MaintainCommand";
+ public const string ManageSnapshotAnswer = "com.cloud.agent.api.ManageSnapshotAnswer";
+ public const string ManageSnapshotCommand = "com.cloud.agent.api.ManageSnapshotCommand";
+ public const string MigrateAnswer = "com.cloud.agent.api.MigrateAnswer";
+ public const string MigrateCommand = "com.cloud.agent.api.MigrateCommand";
+ public const string ModifySshKeysCommand = "com.cloud.agent.api.ModifySshKeysCommand";
+ public const string ModifyStoragePoolAnswer = "com.cloud.agent.api.ModifyStoragePoolAnswer";
+ public const string ModifyStoragePoolCommand = "com.cloud.agent.api.ModifyStoragePoolCommand";
+ public const string NetworkRulesSystemVmCommand = "com.cloud.agent.api.NetworkRulesSystemVmCommand";
+ public const string NetworkRulesVmSecondaryIpCommand = "com.cloud.agent.api.NetworkRulesVmSecondaryIpCommand";
+ public const string PingCommand = "com.cloud.agent.api.PingCommand";
+ public const string PingRoutingCommand = "com.cloud.agent.api.PingRoutingCommand";
+ public const string PingRoutingWithNwGroupsCommand = "com.cloud.agent.api.PingRoutingWithNwGroupsCommand";
+ public const string PingRoutingWithOvsCommand = "com.cloud.agent.api.PingRoutingWithOvsCommand";
+ public const string PingTestCommand = "com.cloud.agent.api.PingTestCommand";
+ public const string PlugNicAnswer = "com.cloud.agent.api.PlugNicAnswer";
+ public const string PlugNicCommand = "com.cloud.agent.api.PlugNicCommand";
+ public const string PoolEjectCommand = "com.cloud.agent.api.PoolEjectCommand";
+ public const string PrepareForMigrationAnswer = "com.cloud.agent.api.PrepareForMigrationAnswer";
+ public const string PrepareForMigrationCommand = "com.cloud.agent.api.PrepareForMigrationCommand";
+ public const string PvlanSetupCommand = "com.cloud.agent.api.PvlanSetupCommand";
+ public const string ReadyAnswer = "com.cloud.agent.api.ReadyAnswer";
+ public const string ReadyCommand = "com.cloud.agent.api.ReadyCommand";
+ public const string RebootAnswer = "com.cloud.agent.api.RebootAnswer";
+ public const string RebootCommand = "com.cloud.agent.api.RebootCommand";
+ public const string RebootRouterCommand = "com.cloud.agent.api.RebootRouterCommand";
+ public const string RevertToVMSnapshotAnswer = "com.cloud.agent.api.RevertToVMSnapshotAnswer";
+ public const string RevertToVMSnapshotCommand = "com.cloud.agent.api.RevertToVMSnapshotCommand";
+ public const string ScaleVmAnswer = "com.cloud.agent.api.ScaleVmAnswer";
+ public const string ScaleVmCommand = "com.cloud.agent.api.ScaleVmCommand";
+ public const string SecurityGroupRuleAnswer = "com.cloud.agent.api.SecurityGroupRuleAnswer";
+ public const string SecurityGroupRulesCmd = "com.cloud.agent.api.SecurityGroupRulesCmd";
+ public const string SetupAnswer = "com.cloud.agent.api.SetupAnswer";
+ public const string SetupCommand = "com.cloud.agent.api.SetupCommand";
+ public const string SetupGuestNetworkAnswer = "com.cloud.agent.api.SetupGuestNetworkAnswer";
+ public const string SetupGuestNetworkCommand = "com.cloud.agent.api.SetupGuestNetworkCommand";
+ public const string StartAnswer = "com.cloud.agent.api.StartAnswer";
+ public const string StartCommand = "com.cloud.agent.api.StartCommand";
+ public const string StartupCommand = "com.cloud.agent.api.StartupCommand";
+ public const string StartupRoutingCommand = "com.cloud.agent.api.StartupRoutingCommand";
+ public const string StartupStorageCommand = "com.cloud.agent.api.StartupStorageCommand";
+ public const string StopAnswer = "com.cloud.agent.api.StopAnswer";
+ public const string StopCommand = "com.cloud.agent.api.StopCommand";
+ public const string StoragePoolInfo = "com.cloud.agent.api.StoragePoolInfo";
+ public const string UnPlugNicAnswer = "com.cloud.agent.api.UnPlugNicAnswer";
+ public const string UnPlugNicCommand = "com.cloud.agent.api.UnPlugNicCommand";
+ public const string UpdateHostPasswordCommand = "com.cloud.agent.api.UpdateHostPasswordCommand";
+ public const string UpgradeSnapshotCommand = "com.cloud.agent.api.UpgradeSnapshotCommand";
+ public const string VmDiskStatsEntry = "com.cloud.agent.api.VmDiskStatsEntry";
+ public const string VmStatsEntry = "com.cloud.agent.api.VmStatsEntry";
+ public const string CheckSshAnswer = "com.cloud.agent.api.check.CheckSshAnswer";
+ public const string CheckSshCommand = "com.cloud.agent.api.check.CheckSshCommand";
+ public const string CheckConsoleProxyLoadCommand = "com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand";
+ public const string ConsoleProxyLoadAnswer = "com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer";
+ public const string WatchConsoleProxyLoadCommand = "com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand";
+ public const string CreateIpAliasCommand = "com.cloud.agent.api.routing.CreateIpAliasCommand";
+ public const string DeleteIpAliasCommand = "com.cloud.agent.api.routing.DeleteIpAliasCommand";
+ public const string DhcpEntryCommand = "com.cloud.agent.api.routing.DhcpEntryCommand";
+ public const string DnsMasqConfigCommand = "com.cloud.agent.api.routing.DnsMasqConfigCommand";
+ public const string IpAliasTO = "com.cloud.agent.api.routing.IpAliasTO";
+ public const string IpAssocAnswer = "com.cloud.agent.api.routing.IpAssocAnswer";
+ public const string IpAssocCommand = "com.cloud.agent.api.routing.IpAssocCommand";
+ public const string IpAssocVpcCommand = "com.cloud.agent.api.routing.IpAssocVpcCommand";
+ public const string LoadBalancerConfigCommand = "com.cloud.agent.api.routing.LoadBalancerConfigCommand";
+ public const string NetworkElementCommand = "com.cloud.agent.api.routing.NetworkElementCommand";
+ public const string RemoteAccessVpnCfgCommand = "com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand";
+ public const string SavePasswordCommand = "com.cloud.agent.api.routing.SavePasswordCommand";
+ public const string SetFirewallRulesAnswer = "com.cloud.agent.api.routing.SetFirewallRulesAnswer";
+ public const string SetFirewallRulesCommand = "com.cloud.agent.api.routing.SetFirewallRulesCommand";
+ public const string SetNetworkACLAnswer = "com.cloud.agent.api.routing.SetNetworkACLAnswer";
+ public const string SetNetworkACLCommand = "com.cloud.agent.api.routing.SetNetworkACLCommand";
+ public const string SetPortForwardingRulesAnswer = "com.cloud.agent.api.routing.SetPortForwardingRulesAnswer";
+ public const string SetPortForwardingRulesCommand = "com.cloud.agent.api.routing.SetPortForwardingRulesCommand";
+ public const string SetPortForwardingRulesVpcCommand = "com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand";
+ public const string SetSourceNatAnswer = "com.cloud.agent.api.routing.SetSourceNatAnswer";
+ public const string SetSourceNatCommand = "com.cloud.agent.api.routing.SetSourceNatCommand";
+ public const string SetStaticNatRulesAnswer = "com.cloud.agent.api.routing.SetStaticNatRulesAnswer";
+ public const string SetStaticNatRulesCommand = "com.cloud.agent.api.routing.SetStaticNatRulesCommand";
+ public const string SetStaticRouteAnswer = "com.cloud.agent.api.routing.SetStaticRouteAnswer";
+ public const string SetStaticRouteCommand = "com.cloud.agent.api.routing.SetStaticRouteCommand";
+ public const string Site2SiteVpnCfgCommand = "com.cloud.agent.api.routing.Site2SiteVpnCfgCommand";
+ public const string VmDataCommand = "com.cloud.agent.api.routing.VmDataCommand";
+ public const string VpnUsersCfgCommand = "com.cloud.agent.api.routing.VpnUsersCfgCommand";
+ public const string CopyVolumeAnswer = "com.cloud.agent.api.storage.CopyVolumeAnswer";
+ public const string CopyVolumeCommand = "com.cloud.agent.api.storage.CopyVolumeCommand";
+ public const string CreateAnswer = "com.cloud.agent.api.storage.CreateAnswer";
+ public const string CreateCommand = "com.cloud.agent.api.storage.CreateCommand";
+ public const string CreatePrivateTemplateAnswer = "com.cloud.agent.api.storage.CreatePrivateTemplateAnswer";
+ public const string DestroyCommand = "com.cloud.agent.api.storage.DestroyCommand";
+ public const string PrimaryStorageDownloadAnswer = "com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer";
+ public const string PrimaryStorageDownloadCommand = "com.cloud.agent.api.storage.PrimaryStorageDownloadCommand";
+ public const string ResizeVolumeAnswer = "com.cloud.agent.api.storage.ResizeVolumeAnswer";
+ public const string ResizeVolumeCommand = "com.cloud.agent.api.storage.ResizeVolumeCommand";
+ public const string FirewallRuleTO = "com.cloud.agent.api.to.FirewallRuleTO";
+ public const string IpAddressTO = "com.cloud.agent.api.to.IpAddressTO";
+ public const string NicTO = "com.cloud.agent.api.to.NicTO";
+ public const string PortForwardingRuleTO = "com.cloud.agent.api.to.PortForwardingRuleTO";
+ public const string S3TO = "com.cloud.agent.api.to.S3TO";
+ public const string NFSTO = "com.cloud.agent.api.to.NfsTO";
+ public const string StaticNatRuleTO = "com.cloud.agent.api.to.StaticNatRuleTO";
+ public const string StorageFilerTO = "com.cloud.agent.api.to.StorageFilerTO";
+ public const string SwiftTO = "com.cloud.agent.api.to.SwiftTO";
+ public const string VirtualMachineTO = "com.cloud.agent.api.to.VirtualMachineTO";
+ public const string VolumeTO = "com.cloud.agent.api.to.VolumeTO";
+ public const string InternalErrorException = "com.cloud.exception.InternalErrorException";
+ public const string HostType = "com.cloud.host.Host.Type";
+ public const string HypervisorType = "com.cloud.hypervisor.Hypervisor.HypervisorType";
+ public const string DnsMasqConfigurator = "com.cloud.network.DnsMasqConfigurator";
+ public const string HAProxyConfigurator = "com.cloud.network.HAProxyConfigurator";
+ public const string LoadBalancerConfigurator = "com.cloud.network.LoadBalancerConfigurator";
+ public const string Networks = "com.cloud.network.Networks";
+ public const string BroadcastDomainType = "com.cloud.network.Networks.BroadcastDomainType";
+ public const string IsolationType = "com.cloud.network.Networks.IsolationType";
+ public const string TrafficType = "com.cloud.network.Networks.TrafficType";
+ public const string PhysicalNetworkSetupInfo = "com.cloud.network.PhysicalNetworkSetupInfo";
+ public const string OvsCreateGreTunnelAnswer = "com.cloud.network.ovs.OvsCreateGreTunnelAnswer";
+ public const string OvsCreateGreTunnelCommand = "com.cloud.network.ovs.OvsCreateGreTunnelCommand";
+ public const string OvsCreateTunnelAnswer = "com.cloud.network.ovs.OvsCreateTunnelAnswer";
+ public const string OvsCreateTunnelCommand = "com.cloud.network.ovs.OvsCreateTunnelCommand";
+ public const string OvsDeleteFlowCommand = "com.cloud.network.ovs.OvsDeleteFlowCommand";
+ public const string OvsDestroyBridgeCommand = "com.cloud.network.ovs.OvsDestroyBridgeCommand";
+ public const string OvsDestroyTunnelCommand = "com.cloud.network.ovs.OvsDestroyTunnelCommand";
+ public const string OvsFetchInterfaceAnswer = "com.cloud.network.ovs.OvsFetchInterfaceAnswer";
+ public const string OvsFetchInterfaceCommand = "com.cloud.network.ovs.OvsFetchInterfaceCommand";
+ public const string OvsSetTagAndFlowAnswer = "com.cloud.network.ovs.OvsSetTagAndFlowAnswer";
+ public const string OvsSetTagAndFlowCommand = "com.cloud.network.ovs.OvsSetTagAndFlowCommand";
+ public const string OvsSetupBridgeCommand = "com.cloud.network.ovs.OvsSetupBridgeCommand";
+ public const string FirewallRule = "com.cloud.network.rules.FirewallRule";
+ public const string ServerResource = "com.cloud.resource.ServerResource";
+ public const string HypervisorResource = "com.cloud.resource.hypervisor.HypervisorResource";
+ public const string Storage = "com.cloud.storage.Storage";
+ public const string ImageFormat = "com.cloud.storage.Storage.ImageFormat";
+ public const string StoragePoolType = "com.cloud.storage.Storage.StoragePoolType";
+ public const string Volume = "com.cloud.storage.Volume";
+ public const string VolumeVO = "com.cloud.storage.VolumeVO";
+ public const string StorageSubsystemCommandHandler = "com.cloud.storage.resource.StorageSubsystemCommandHandler";
+ public const string StorageSubsystemCommandHandlerBase = "com.cloud.storage.resource.StorageSubsystemCommandHandlerBase";
+ public const string TemplateProp = "com.cloud.storage.template.TemplateProp";
+ public const string BootloaderType = "com.cloud.template.VirtualMachineTemplate.BootloaderType";
+ public const string VolumeObjectTO = "org.apache.cloudstack.storage.to.VolumeObjectTO";
+ public const string TemplateObjectTO = "org.apache.cloudstack.storage.to.TemplateObjectTO";
+ public const string PrimaryDataStoreTO = "org.apache.cloudstack.storage.to.PrimaryDataStoreTO";
+ public const string AttachAnswer = "org.apache.cloudstack.storage.command.AttachAnswer";
+ public const string AttachCommand = "org.apache.cloudstack.storage.command.AttachCommand";
+ public const string AttachPrimaryDataStoreAnswer = "org.apache.cloudstack.storage.command.AttachPrimaryDataStoreAnswer";
+ public const string AttachPrimaryDataStoreCmd = "org.apache.cloudstack.storage.command.AttachPrimaryDataStoreCmd";
+ public const string CopyCmdAnswer = "org.apache.cloudstack.storage.command.CopyCmdAnswer";
+ public const string CopyCommand = "org.apache.cloudstack.storage.command.CopyCommand";
+ public const string CreateObjectAnswer = "org.apache.cloudstack.storage.command.CreateObjectAnswer";
+ public const string CreateObjectCommand = "org.apache.cloudstack.storage.command.CreateObjectCommand";
+ public const string DeleteCommand = "org.apache.cloudstack.storage.command.DeleteCommand";
+ public const string DettachAnswer = "org.apache.cloudstack.storage.command.DettachAnswer";
+ public const string DettachCommand = "org.apache.cloudstack.storage.command.DettachCommand";
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj
new file mode 100644
index 00000000000..05a0f513d8b
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj
@@ -0,0 +1,103 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C963DFFF-65BA-4E71-ADA5-526A4DA4E0B2}
+ Library
+ Properties
+ HypervResource
+ HypervResource
+ v4.5
+ 512
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ bin\NoUnitTestsDebug\
+ DEBUG;TRACE
+ full
+ AnyCPU
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+ bin\NoUnitTests\
+ TRACE
+ true
+ pdbonly
+ AnyCPU
+ prompt
+ MinimumRecommendedRules.ruleset
+
+
+
+ ..\packages\AWSSDK.1.5.23.0\lib\AWSSDK.dll
+
+
+ ..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll
+
+
+ ..\packages\log4net.2.0.0\lib\net40-full\log4net.dll
+
+
+ ..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {db824727-bdc3-437c-a364-7a811d8a160f}
+ WmiWrappers
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
new file mode 100644
index 00000000000..ed3736b072b
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
@@ -0,0 +1,1459 @@
+// 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.
+
+using Amazon;
+using Amazon.S3;
+using Amazon.S3.Model;
+using log4net;
+using Microsoft.CSharp.RuntimeBinder;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Security.Cryptography;
+using System.Security.Principal;
+using System.Web.Http;
+
+namespace HypervResource
+{
+
+ public struct HypervResourceControllerConfig
+ {
+ private string privateIpAddress;
+ private static ILog logger = LogManager.GetLogger(typeof(HypervResourceControllerConfig));
+
+ public string PrivateIpAddress
+ {
+ get
+ {
+ return privateIpAddress;
+ }
+ set
+ {
+ ValidateIpAddress(value);
+ privateIpAddress = value;
+ System.Net.NetworkInformation.NetworkInterface nic = HypervResourceController.GetNicInfoFromIpAddress(privateIpAddress, out PrivateNetmask);
+ PrivateMacAddress = nic.GetPhysicalAddress().ToString();
+ }
+ }
+
+ private static void ValidateIpAddress(string value)
+ {
+ // Convert to IP address
+ IPAddress ipAddress;
+ if (!IPAddress.TryParse(value, out ipAddress))
+ {
+ String errMsg = "Invalid PrivateIpAddress: " + value;
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+ }
+ public string GatewayIpAddress;
+ public string PrivateMacAddress;
+ public string PrivateNetmask;
+ public string StorageNetmask;
+ public string StorageMacAddress;
+ public string StorageIpAddress;
+ public long RootDeviceReservedSpaceBytes;
+ public string RootDeviceName;
+ public ulong ParentPartitionMinMemoryMb;
+ public string LocalSecondaryStoragePath;
+ public string systemVmIso;
+ }
+
+ ///
+ /// Supports one HTTP GET and multiple HTTP POST URIs
+ ///
+ ///
+ ///
+ /// POST takes dynamic to allow it to receive JSON without concern for what is the underlying object.
+ /// E.g. http://stackoverflow.com/questions/14071715/passing-dynamic-json-object-to-web-api-newtonsoft-example
+ /// and http://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object
+ /// Use ActionName attribute to allow multiple POST URLs, one for each supported command
+ /// E.g. http://stackoverflow.com/a/12703423/939250
+ /// Strictly speaking, this goes against the purpose of an ApiController, which is to provide one GET/POST/PUT/DELETE, etc.
+ /// However, it reduces the amount of code by removing the need for a switch according to the incoming command type.
+ /// http://weblogs.asp.net/fredriknormen/archive/2012/06/11/asp-net-web-api-exception-handling.aspx
+ ///
+ ///
+ /// Exceptions handled on command by command basis rather than globally to allow details of the command
+ /// to be reflected in the response. Default error handling is in the catch for Exception, but
+ /// other exception types may be caught where the feedback would be different.
+ /// NB: global alternatives discussed at
+ /// http://weblogs.asp.net/fredriknormen/archive/2012/06/11/asp-net-web-api-exception-handling.aspx
+ ///
+ ///
+ public class HypervResourceController : ApiController
+ {
+ public static void Configure(HypervResourceControllerConfig config)
+ {
+ HypervResourceController.config = config;
+ wmiCallsV2 = new WmiCallsV2();
+ }
+
+ public static HypervResourceControllerConfig config = new HypervResourceControllerConfig();
+
+ private static ILog logger = LogManager.GetLogger(typeof(HypervResourceController));
+ private static string systemVmIso;
+
+ public static void Initialize()
+ {
+ }
+
+ public static IWmiCallsV2 wmiCallsV2 { get; set;}
+
+ // GET api/HypervResource
+ public string Get()
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ return "HypervResource controller running, use POST to send JSON encoded RPCs"; ;
+ }
+ }
+
+ ///
+ /// NOP - placeholder for future setup, e.g. delete existing VMs or Network ports
+ /// POST api/HypervResource/SetupCommand
+ ///
+ ///
+ ///
+ /// TODO: produce test
+ [HttpPost]
+ [ActionName(CloudStackTypes.SetupCommand)]
+ public JContainer SetupCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.SetupCommand + cmd.ToString());
+
+ string details = null;
+ bool result = false;
+
+ try
+ {
+ NFSTO share = new NFSTO();
+ String uriStr = (String)cmd.secondaryStorage;
+ share.uri = new Uri(uriStr);
+
+ string systemVmIso = (string)cmd.systemVmIso;
+ string defaultDataPath = wmiCallsV2.GetDefaultDataRoot();
+ string isoPath = Path.Combine(defaultDataPath, Path.GetFileName(systemVmIso));
+ if (!File.Exists(isoPath))
+ {
+ logger.Info("File " + isoPath + " not found. Copying it from the secondary share.");
+ Utils.DownloadCifsFileToLocalFile(systemVmIso, share, isoPath);
+ }
+ HypervResourceController.systemVmIso = isoPath;
+ result = true;
+ }
+ catch (Exception sysEx)
+ {
+ details = CloudStackTypes.SetupCommand + " failed due to " + sysEx.Message;
+ logger.Error(details, sysEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = "success - NOP",
+ _reconnect = false
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.SetupAnswer);
+ }
+ }
+
+ // POST api/HypervResource/AttachCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.AttachCommand)]
+ public JContainer AttachCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.AttachCommand + cmd.ToString());
+
+ string details = null;
+ bool result = false;
+
+ try
+ {
+ string vmName = (string)cmd.vmName;
+ result = true;
+ }
+ catch (Exception sysEx)
+ {
+ details = CloudStackTypes.AttachCommand + " failed due to " + sysEx.Message;
+ logger.Error(details, sysEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.AttachAnswer);
+ }
+ }
+
+ // POST api/HypervResource/DetachCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.DettachCommand)]
+ public JContainer DetachCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.DettachCommand + cmd.ToString());
+
+ string details = null;
+ bool result = false;
+
+ try
+ {
+ string vmName = (string)cmd.vmName;
+ result = true;
+ }
+ catch (Exception sysEx)
+ {
+ details = CloudStackTypes.DettachCommand + " failed due to " + sysEx.Message;
+ logger.Error(details, sysEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.DettachAnswer);
+ }
+ }
+
+ // POST api/HypervResource/DestroyCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.DestroyCommand)]
+ public JContainer DestroyCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.DestroyCommand + cmd.ToString());
+
+ string details = null;
+ bool result = false;
+
+ try
+ {
+ // Assert
+ String errMsg = "No 'volume' details in " + CloudStackTypes.DestroyCommand + " " + cmd.ToString();
+ if (cmd.volume == null)
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+
+ // Assert
+ errMsg = "No valide path in DestroyCommand in " + CloudStackTypes.DestroyCommand + " " + (String)cmd.ToString();
+ if (cmd.volume.path == null)
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+
+ String path = (string)cmd.volume.path;
+ if (!File.Exists(path))
+ {
+ logger.Info(CloudStackTypes.DestroyCommand + ", but volume at pass already deleted " + path);
+ }
+
+ string vmName = (string)cmd.vmName;
+ if (!string.IsNullOrEmpty(vmName) && File.Exists(path))
+ {
+ // Make sure that this resource is removed from the VM
+ wmiCallsV2.DetachDisk(vmName, path);
+ }
+
+ File.Delete(path);
+ result = true;
+ }
+ catch (Exception sysEx)
+ {
+ details = CloudStackTypes.DestroyCommand + " failed due to " + sysEx.Message;
+ logger.Error(details, sysEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+ }
+
+ private static JArray ReturnCloudStackTypedJArray(object ansContent, string ansType)
+ {
+ JObject ansObj = Utils.CreateCloudStackObject(ansType, ansContent);
+ JArray answer = new JArray(ansObj);
+ logger.Info(ansObj.ToString());
+ return answer;
+ }
+
+ // POST api/HypervResource/CreateCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.CreateCommand)]
+ public JContainer CreateCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CreateCommand + cmd.ToString());
+
+ string details = null;
+ bool result = false;
+ VolumeInfo volume = new VolumeInfo();
+
+ try
+ {
+ string diskType = cmd.diskCharacteristics.type;
+ ulong disksize = cmd.diskCharacteristics.size;
+ string templateUri = cmd.templateUrl;
+
+ // assert: valid storagepool?
+ string poolTypeStr = cmd.pool.type;
+ string poolLocalPath = cmd.pool.path;
+ string poolUuid = cmd.pool.uuid;
+ string newVolPath = null;
+ long volId = cmd.volId;
+ string newVolName = null;
+
+ if (ValidStoragePool(poolTypeStr, poolLocalPath, poolUuid, ref details))
+ {
+ // No template URI? Its a blank disk.
+ if (string.IsNullOrEmpty(templateUri))
+ {
+ // assert
+ VolumeType volType;
+ if (!Enum.TryParse(diskType, out volType) && volType != VolumeType.DATADISK)
+ {
+ details = "Cannot create volumes of type " + (string.IsNullOrEmpty(diskType) ? "NULL" : diskType);
+ }
+ else
+ {
+ newVolName = cmd.diskCharacteristics.name;
+ newVolPath = Path.Combine(poolLocalPath, newVolName, diskType.ToLower());
+ // TODO: make volume format and block size configurable
+ wmiCallsV2.CreateDynamicVirtualHardDisk(disksize, newVolPath);
+ if (File.Exists(newVolPath))
+ {
+ result = true;
+ }
+ else
+ {
+ details = "Failed to create DATADISK with name " + newVolName;
+ }
+ }
+ }
+ else
+ {
+ // TODO: Does this always work, or do I need to download template at times?
+ if (templateUri.Contains("/") || templateUri.Contains("\\"))
+ {
+ details = "Problem with templateURL " + templateUri +
+ " the URL should be volume UUID in primary storage created by previous PrimaryStorageDownloadCommand";
+ logger.Error(details);
+ }
+ else
+ {
+ logger.Debug("Template's name in primary store should be " + templateUri);
+ // HypervPhysicalDisk BaseVol = primaryPool.getPhysicalDisk(tmplturl);
+ FileInfo srcFileInfo = new FileInfo(templateUri);
+ newVolName = Guid.NewGuid() + srcFileInfo.Extension;
+ newVolPath = Path.Combine(poolLocalPath, newVolName);
+ logger.Debug("New volume will be at " + newVolPath);
+ string oldVolPath = Path.Combine(poolLocalPath, templateUri);
+ File.Copy(oldVolPath, newVolPath);
+ if (File.Exists(newVolPath))
+ {
+ result = true;
+ }
+ else
+ {
+ details = "Failed to create DATADISK with name " + newVolName;
+ }
+ }
+ volume = new VolumeInfo(
+ volId, diskType,
+ poolTypeStr, poolUuid, newVolName,
+ newVolPath, newVolPath, (long)disksize, null);
+ }
+ }
+ }
+ catch (Exception sysEx)
+ {
+ // TODO: consider this as model for error processing in all commands
+ details = CloudStackTypes.CreateCommand + " failed due to " + sysEx.Message;
+ logger.Error(details, sysEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ volume = volume
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CreateAnswer);
+ }
+ }
+
+ // POST api/HypervResource/PrimaryStorageDownloadCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.PrimaryStorageDownloadCommand)]
+ public JContainer PrimaryStorageDownloadCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.PrimaryStorageDownloadCommand + cmd.ToString());
+ string details = null;
+ bool result = false;
+ long size = 0;
+ string newCopyFileName = null;
+
+ string poolLocalPath = cmd.localPath;
+ string poolUuid = cmd.poolUuid;
+ if (!Directory.Exists(poolLocalPath))
+ {
+ details = "None existent local path " + poolLocalPath;
+ }
+ else
+ {
+ // Compose name for downloaded file.
+ string sourceUrl = cmd.url;
+ if (sourceUrl.ToLower().EndsWith(".vhd"))
+ {
+ newCopyFileName = Guid.NewGuid() + ".vhd";
+ }
+ if (sourceUrl.ToLower().EndsWith(".vhdx"))
+ {
+ newCopyFileName = Guid.NewGuid() + ".vhdx";
+ }
+
+ // assert
+ if (newCopyFileName == null)
+ {
+ details = CloudStackTypes.PrimaryStorageDownloadCommand + " Invalid file extension for hypervisor type in source URL " + sourceUrl;
+ logger.Error(details);
+ }
+ else
+ {
+ try
+ {
+ FileInfo newFile;
+ if (CopyURI(sourceUrl, newCopyFileName, poolLocalPath, out newFile, ref details))
+ {
+ size = newFile.Length;
+ result = true;
+ }
+ }
+ catch (System.Exception ex)
+ {
+ details = CloudStackTypes.PrimaryStorageDownloadCommand + " Cannot download source URL " + sourceUrl + " due to " + ex.Message;
+ logger.Error(details, ex);
+ }
+ }
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ templateSize = size,
+ installPath = newCopyFileName
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PrimaryStorageDownloadAnswer);
+ }
+ }
+
+ private static bool ValidStoragePool(string poolTypeStr, string poolLocalPath, string poolUuid, ref string details)
+ {
+ StoragePoolType poolType;
+ if (!Enum.TryParse(poolTypeStr, out poolType) || poolType != StoragePoolType.Filesystem)
+ {
+ details = "Primary storage pool " + poolUuid + " type " + poolType + " local path " + poolLocalPath + " has invalid StoragePoolType";
+ logger.Error(details);
+ return false;
+ }
+ else if (!Directory.Exists(poolLocalPath))
+ {
+ details = "Primary storage pool " + poolUuid + " type " + poolType + " local path " + poolLocalPath + " has invalid local path";
+ logger.Error(details);
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Exceptions to watch out for:
+ /// Exceptions related to URI creation
+ /// System.SystemException
+ /// +-System.ArgumentNullException
+ /// +-System.FormatException
+ /// +-System.UriFormatException
+ ///
+ /// Exceptions related to NFS URIs
+ /// System.SystemException
+ /// +-System.NotSupportedException
+ /// +-System.ArgumentException
+ /// +-System.ArgumentNullException
+ /// +-System.Security.SecurityException;
+ /// +-System.UnauthorizedAccessException
+ /// +-System.IO.IOException
+ /// +-System.IO.PathTooLongException
+ ///
+ /// Exceptions related to HTTP URIs
+ /// System.SystemException
+ /// +-System.InvalidOperationException
+ /// +-System.Net.WebException
+ /// +-System.NotSupportedException
+ /// +-System.ArgumentNullException
+ ///
+ ///
+ ///
+ ///
+ ///
+ private bool CopyURI(string sourceUri, string newCopyFileName, string poolLocalPath, out FileInfo newFile, ref string details)
+ {
+ Uri source = new Uri(sourceUri);
+ String destFilePath = Path.Combine(poolLocalPath, newCopyFileName);
+ string[] pathSegments = source.Segments;
+ String templateUUIDandExtension = pathSegments[pathSegments.Length - 1];
+ newFile = new FileInfo(destFilePath);
+
+ // NFS URI assumed to already be mounted locally. Mount location given by settings.
+ if (source.Scheme.ToLower().Equals("nfs"))
+ {
+ String srcDiskPath = Path.Combine(HypervResourceController.config.LocalSecondaryStoragePath, templateUUIDandExtension);
+ String taskMsg = "Copy NFS url in " + sourceUri + " at " + srcDiskPath + " to pool " + poolLocalPath;
+ logger.Debug(taskMsg);
+ File.Copy(srcDiskPath, destFilePath);
+ }
+ else if (source.Scheme.ToLower().Equals("http") || source.Scheme.ToLower().Equals("https"))
+ {
+ System.Net.WebClient webclient = new WebClient();
+ webclient.DownloadFile(source, destFilePath);
+ }
+ else
+ {
+ details = "Unsupported URI scheme " + source.Scheme.ToLower() + " in source URI " + sourceUri;
+ logger.Error(details);
+ return false;
+ }
+
+ if (!File.Exists(destFilePath))
+ {
+ details = "Filed to copy " + sourceUri + " to primary pool destination " + destFilePath;
+ logger.Error(details);
+ return false;
+ }
+ return true;
+ }
+
+ // POST api/HypervResource/CheckHealthCommand
+ // TODO: create test
+ [HttpPost]
+ [ActionName(CloudStackTypes.CheckHealthCommand)]
+ public JContainer CheckHealthCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CheckHealthCommand + cmd.ToString());
+ object ansContent = new
+ {
+ result = true,
+ details = "resource is alive"
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckHealthAnswer);
+ }
+ }
+
+ // POST api/HypervResource/CheckSshCommand
+ // TODO: create test
+ [HttpPost]
+ [ActionName(CloudStackTypes.CheckSshCommand)]
+ public JContainer CheckSshCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CheckSshCommand + cmd.ToString());
+ object ansContent = new
+ {
+ result = true,
+ details = "NOP, TODO: implement properly"
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckSshAnswer);
+ }
+ }
+
+ // POST api/HypervResource/CheckVirtualMachineCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.CheckVirtualMachineCommand)]
+ public JContainer CheckVirtualMachineCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CheckVirtualMachineCommand + cmd.ToString());
+ string details = null;
+ bool result = false;
+ string vmName = cmd.vmName;
+ string state = null;
+
+ // TODO: Look up the VM, convert Hyper-V state to CloudStack version.
+ var sys = wmiCallsV2.GetComputerSystem(vmName);
+ if (sys == null)
+ {
+ details = CloudStackTypes.CheckVirtualMachineCommand + " requested unknown VM " + vmName;
+ logger.Error(details);
+ }
+ else
+ {
+ state = EnabledState.ToString(sys.EnabledState); // TODO: V2 changes?
+ result = true;
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ state = state
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckVirtualMachineAnswer);
+ }
+ }
+
+ // POST api/HypervResource/DeleteStoragePoolCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.DeleteStoragePoolCommand)]
+ public JContainer DeleteStoragePoolCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.DeleteStoragePoolCommand + cmd.ToString());
+ object ansContent = new
+ {
+ result = true,
+ details = "Current implementation does not delete local path corresponding to storage pool!"
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+ }
+
+ ///
+ /// NOP - legacy command -
+ /// POST api/HypervResource/CreateStoragePoolCommand
+ ///
+ ///
+ ///
+ [HttpPost]
+ [ActionName(CloudStackTypes.CreateStoragePoolCommand)]
+ public JContainer CreateStoragePoolCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CreateStoragePoolCommand + cmd.ToString());
+ object ansContent = new
+ {
+ result = true,
+ details = "success - NOP"
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+ }
+
+ // POST api/HypervResource/ModifyStoragePoolCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.ModifyStoragePoolCommand)]
+ public JContainer ModifyStoragePoolCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.ModifyStoragePoolCommand + cmd.ToString());
+ string details = null;
+ string localPath;
+ object ansContent;
+
+ bool result = ValidateStoragePoolCommand(cmd, out localPath, ref details);
+ if (!result)
+ {
+ ansContent = new
+ {
+ result = result,
+ details = details
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+
+ var tInfo = new Dictionary();
+ long capacityBytes;
+ long availableBytes;
+ GetCapacityForLocalPath(localPath, out capacityBytes, out availableBytes);
+
+ String uuid = null;
+ var poolInfo = new
+ {
+ uuid = uuid,
+ host = cmd.pool.host,
+ localPath = cmd.pool.host,
+ hostPath = cmd.localPath,
+ poolType = cmd.pool.type,
+ capacityBytes = capacityBytes,
+ availableBytes = availableBytes
+ };
+
+ ansContent = new
+ {
+ result = result,
+ details = details,
+ templateInfo = tInfo,
+ poolInfo = poolInfo
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ModifyStoragePoolAnswer);
+ }
+ }
+
+ private bool ValidateStoragePoolCommand(dynamic cmd, out string localPath, ref string details)
+ {
+ dynamic pool = cmd.pool;
+ string poolTypeStr = pool.type;
+ StoragePoolType poolType;
+ localPath = cmd.localPath;
+ if (!Enum.TryParse(poolTypeStr, out poolType) || poolType != StoragePoolType.Filesystem)
+ {
+ details = "Request to create / modify unsupported pool type: " + (poolTypeStr == null ? "NULL" : poolTypeStr) + "in cmd " + JsonConvert.SerializeObject(cmd);
+ logger.Error(details);
+ return false;
+ }
+ if (!Directory.Exists(localPath))
+ {
+ details = "Request to create / modify unsupported StoragePoolType.Filesystem with non-existent path:" + (localPath == null ? "NULL" : localPath) + "in cmd " + JsonConvert.SerializeObject(cmd);
+ logger.Error(details);
+ return false;
+ }
+ return true;
+ }
+
+
+ // POST api/HypervResource/CleanupNetworkRulesCmd
+ [HttpPost]
+ [ActionName(CloudStackTypes.CleanupNetworkRulesCmd)]
+ public JContainer CleanupNetworkRulesCmd([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CleanupNetworkRulesCmd + cmd.ToString());
+ object ansContent = new
+ {
+ result = false,
+ details = "nothing to cleanup in our current implementation"
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+ }
+
+ // POST api/HypervResource/CheckNetworkCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.CheckNetworkCommand)]
+ public JContainer CheckNetworkCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.CheckNetworkCommand + cmd.ToString());
+ object ansContent = new
+ {
+ result = true,
+ details = (string)null
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckNetworkAnswer);
+ }
+ }
+
+ // POST api/HypervResource/ReadyCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.ReadyCommand)]
+ public JContainer ReadyCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.ReadyCommand + cmd.ToString());
+ object ansContent = new
+ {
+ result = true,
+ details = (string)null
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ReadyAnswer);
+ }
+
+ }
+
+ // POST api/HypervResource/StartCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.StartCommand)]
+ public JContainer StartCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.StartCommand + cmd.ToString()); // TODO: Security hole? VM data printed to log
+ string details = null;
+ bool result = false;
+
+ try
+ {
+ wmiCallsV2.DeployVirtualMachine(cmd, systemVmIso);
+ result = true;
+ }
+ catch (Exception wmiEx)
+ {
+ details = CloudStackTypes.StartCommand + " fail on exception" + wmiEx.Message;
+ logger.Error(details, wmiEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ vm = cmd.vm
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.StartAnswer);
+ }
+ }
+
+ // POST api/HypervResource/StopCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.StopCommand)]
+ public JContainer StopCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.StopCommand + cmd.ToString());
+ string details = null;
+ bool result = false;
+
+ try
+ {
+ wmiCallsV2.DestroyVm(cmd);
+ result = true;
+ }
+ catch (Exception wmiEx)
+ {
+ details = CloudStackTypes.StopCommand + " fail on exception" + wmiEx.Message;
+ logger.Error(details, wmiEx);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ vm = cmd.vm
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.StopAnswer);
+ }
+ }
+
+ // POST api/HypervResource/MaintainCommand
+ // TODO: should this be a NOP?
+ [HttpPost]
+ [ActionName(CloudStackTypes.MaintainCommand)]
+ public JContainer MaintainCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.MaintainCommand + cmd.ToString());
+
+ object ansContent = new
+ {
+ result = true,
+ details = "success - NOP for MaintainCommand",
+ _reconnect = false
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MaintainAnswer);
+ }
+ }
+
+ // POST api/HypervResource/PingRoutingCommand
+ // TODO: should this be a NOP?
+ [HttpPost]
+ [ActionName(CloudStackTypes.PingRoutingCommand)]
+ public JContainer PingRoutingCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.PingRoutingCommand + cmd.ToString());
+
+ object ansContent = new
+ {
+ result = true,
+ details = "success - NOP for PingRoutingCommand",
+ _reconnect = false
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+ }
+
+ // POST api/HypervResource/PingCommand
+ // TODO: should this be a NOP?
+ [HttpPost]
+ [ActionName(CloudStackTypes.PingCommand)]
+ public JContainer PingCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.PingCommand + cmd.ToString());
+
+ object ansContent = new
+ {
+ result = true,
+ details = "success - NOP for PingCommand",
+ _reconnect = false
+ };
+
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer);
+ }
+ }
+
+ // POST api/HypervResource/GetVmStatsCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.GetVmStatsCommand)]
+ public JContainer GetVmStatsCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.GetVmStatsCommand + cmd.ToString());
+ bool result = false;
+ string details = null;
+ JArray vmNamesJson = cmd.vmNames;
+ string[] vmNames = vmNamesJson.ToObject();
+ Dictionary vmProcessorInfo = new Dictionary(vmNames.Length);
+
+ var vmsToInspect = new List();
+ foreach (var vmName in vmNames)
+ {
+ var sys = wmiCallsV2.GetComputerSystem(vmName);
+ if (sys == null)
+ {
+ logger.InfoFormat("GetVmStatsCommand requested unknown VM {0}", vmNames);
+ continue;
+ }
+ var sysInfo = wmiCallsV2.GetVmSettings(sys);
+ vmsToInspect.Add(sysInfo.Path);
+ }
+
+ wmiCallsV2.GetSummaryInfo(vmProcessorInfo, vmsToInspect);
+
+ // TODO: Network usage comes from Performance Counter API; however it is only available in kb/s, and not in total terms.
+ // Curious about these? Use perfmon to inspect them, e.g. http://msdn.microsoft.com/en-us/library/xhcx5a20%28v=vs.100%29.aspx
+ // Recent post on these counter at http://blogs.technet.com/b/cedward/archive/2011/07/19/hyper-v-networking-optimizations-part-6-of-6-monitoring-hyper-v-network-consumption.aspx
+ result = true;
+
+ object ansContent = new
+ {
+ vmInfos = vmProcessorInfo,
+ result = result,
+ details = details,
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVmStatsAnswer);
+ }
+ }
+
+ // POST api/HypervResource/CopyCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.CopyCommand)]
+ public JContainer CopyCommand(dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ // Log command *after* we've removed security details from the command.
+
+ bool result = false;
+ string details = null;
+ object newData = null;
+
+ try
+ {
+ dynamic timeout = cmd.wait; // TODO: Useful?
+
+ TemplateObjectTO srcTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.srcTO);
+ TemplateObjectTO destTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.destTO);
+ VolumeObjectTO destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.destTO);
+
+ logger.Info(CloudStackTypes.CopyCommand + cmd.ToString());
+
+ // Already exists?
+ if (destTemplateObjectTO != null &&
+ File.Exists(destTemplateObjectTO.FullFileName) &&
+ !String.IsNullOrEmpty(destTemplateObjectTO.checksum))
+ {
+ // TODO: checksum fails us, because it is of the compressed image.
+ // ASK: should we store the compressed or uncompressed version or is the checksum not calculated correctly?
+ result = VerifyChecksum(destTemplateObjectTO.FullFileName, destTemplateObjectTO.checksum);
+ }
+
+ // Do we have to create a new one?
+ if (!result)
+ {
+ // Create local copy of a template?
+ if (srcTemplateObjectTO != null && destTemplateObjectTO != null)
+ {
+ // S3 download to primary storage?
+ // NFS provider download to primary storage?
+ if ((srcTemplateObjectTO.s3DataStoreTO != null || srcTemplateObjectTO.nfsDataStoreTO != null) && destTemplateObjectTO.primaryDataStore != null)
+ {
+ string destFile = destTemplateObjectTO.FullFileName;
+
+ if (File.Exists(destFile))
+ {
+ logger.Info("Deleting existing file " + destFile);
+ File.Delete(destFile);
+ }
+
+ if (srcTemplateObjectTO.s3DataStoreTO != null)
+ {
+ // Download from S3 to destination data storage
+ DownloadS3ObjectToFile(srcTemplateObjectTO.path, srcTemplateObjectTO.s3DataStoreTO, destFile);
+ }
+ else if (srcTemplateObjectTO.nfsDataStoreTO != null)
+ {
+ // Download from S3 to destination data storage
+ Utils.DownloadCifsFileToLocalFile(srcTemplateObjectTO.path, srcTemplateObjectTO.nfsDataStoreTO, destFile);
+ }
+
+ // Uncompress, as required
+ if (srcTemplateObjectTO.path.EndsWith(".bz2"))
+ {
+ String uncompressedFile = destFile + ".tmp";
+ String compressedFile = destFile;
+ using (var uncompressedOutStrm = new FileStream(uncompressedFile, FileMode.CreateNew, FileAccess.Write))
+ {
+ using (var compressedInStrm = new FileStream(destFile, FileMode.Open, FileAccess.Read))
+ {
+ using (var bz2UncompressorStrm = new Ionic.BZip2.BZip2InputStream(compressedInStrm, true) /* outer 'using' statement will close FileStream*/ )
+ {
+ int count = 0;
+ int bufsize = 1024 * 1024;
+ byte[] buf = new byte[bufsize];
+
+ // EOF returns -1, see http://dotnetzip.codeplex.com/workitem/16069
+ while (0 < (count = bz2UncompressorStrm.Read(buf, 0, bufsize)))
+ {
+ uncompressedOutStrm.Write(buf, 0, count);
+ }
+ }
+ }
+ }
+ File.Delete(compressedFile);
+ File.Move(uncompressedFile, compressedFile);
+ if (File.Exists(uncompressedFile))
+ {
+ String errMsg = "Extra file left around called " + uncompressedFile + " when creating " + destFile;
+ logger.Error(errMsg);
+ throw new IOException(errMsg);
+ }
+ }
+
+ // assert
+ if (!File.Exists(destFile))
+ {
+ String errMsg = "Failed to create " + destFile + " , because the file is missing";
+ logger.Error(errMsg);
+ throw new IOException(errMsg);
+ }
+
+ newData = cmd.destTO;
+ result = true;
+ }
+ else
+ {
+ details = "Data store combination not supported";
+ }
+ }
+ // Create volume from a template?
+ else if (srcTemplateObjectTO != null && destVolumeObjectTO != null)
+ {
+ if (destVolumeObjectTO.format == null)
+ {
+ destVolumeObjectTO.format = srcTemplateObjectTO.format;
+ }
+ string destFile = destVolumeObjectTO.FullFileName;
+ string srcFile = srcTemplateObjectTO.FullFileName;
+
+ if (!File.Exists(srcFile))
+ {
+ details = "Local template file missing from " + srcFile;
+ }
+ else
+ {
+ if (File.Exists(destFile))
+ {
+ logger.Info("Deleting existing file " + destFile);
+ File.Delete(destFile);
+ }
+
+ // TODO: thin provision instead of copying the full file.
+ File.Copy(srcFile, destFile);
+ newData = cmd.destTO;
+ result = true;
+ }
+ }
+ else
+ {
+ details = "Data store combination not supported";
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // Test by providing wrong key
+ details = CloudStackTypes.CopyCommand + " failed on exception, " + ex.Message;
+ logger.Error(details, ex);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ newData = cmd.destTO
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CopyCmdAnswer);
+ }
+ }
+
+ private static bool VerifyChecksum(string destFile, string checksum)
+ {
+ string localChecksum = BitConverter.ToString(CalcFileChecksum(destFile)).Replace("-", "").ToLower();
+ logger.Debug("Checksum of " + destFile + " is " + checksum);
+ if (checksum.Equals(localChecksum))
+ {
+ return true;
+ }
+ return true;
+ }
+
+ ///
+ /// Match implmentation of DownloadManagerImpl.computeCheckSum
+ ///
+ ///
+ ///
+ private static byte[] CalcFileChecksum(string destFile)
+ {
+ // TODO: Add unit test to verify that checksum algorithm has not changed.
+ using (MD5 md5 = MD5.Create())
+ {
+ using (FileStream stream = File.OpenRead(destFile))
+ {
+ return md5.ComputeHash(stream);
+ }
+ }
+ }
+
+ private static void DownloadS3ObjectToFile(string srcObjectKey, S3TO srcS3TO, string destFile)
+ {
+ AmazonS3Config S3Config = new AmazonS3Config
+ {
+ ServiceURL = srcS3TO.endpoint,
+ CommunicationProtocol = Amazon.S3.Model.Protocol.HTTP
+ };
+
+ if (srcS3TO.httpsFlag)
+ {
+ S3Config.CommunicationProtocol = Protocol.HTTPS;
+ }
+
+ try
+ {
+ using (AmazonS3 client = Amazon.AWSClientFactory.CreateAmazonS3Client(srcS3TO.accessKey, srcS3TO.secretKey, S3Config))
+ {
+ GetObjectRequest getObjectRequest = new GetObjectRequest().WithBucketName(srcS3TO.bucketName).WithKey(srcObjectKey);
+
+ using (S3Response getObjectResponse = client.GetObject(getObjectRequest))
+ {
+ using (Stream s = getObjectResponse.ResponseStream)
+ {
+ using (FileStream fs = new FileStream(destFile, FileMode.Create, FileAccess.Write))
+ {
+ byte[] data = new byte[524288];
+ int bytesRead = 0;
+ do
+ {
+ bytesRead = s.Read(data, 0, data.Length);
+ fs.Write(data, 0, bytesRead);
+ }
+ while (bytesRead > 0);
+ fs.Flush();
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ string errMsg = "Download from S3 url" + srcS3TO.endpoint + " said: " + ex.Message;
+ logger.Error(errMsg, ex);
+ throw new Exception(errMsg, ex);
+ }
+ }
+
+ // POST api/HypervResource/GetStorageStatsCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.GetStorageStatsCommand)]
+ public JContainer GetStorageStatsCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.GetStorageStatsCommand + cmd.ToString());
+ bool result = false;
+ string details = null;
+ long capacity = 0;
+ long available = 0;
+ long used = 0;
+ try
+ {
+ string localPath = (string)cmd.localPath;
+ GetCapacityForLocalPath(localPath, out capacity, out available);
+ used = capacity - available;
+ result = true;
+ logger.Debug(CloudStackTypes.GetStorageStatsCommand + " set used bytes to " + used);
+ }
+ catch (Exception ex)
+ {
+ details = CloudStackTypes.GetStorageStatsCommand + " failed on exception" + ex.Message;
+ logger.Error(details, ex);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ details = details,
+ capacity = capacity,
+ used = used
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetStorageStatsAnswer);
+ }
+ }
+
+ // POST api/HypervResource/GetHostStatsCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.GetHostStatsCommand)]
+ public JContainer GetHostStatsCommand([FromBody]dynamic cmd)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(CloudStackTypes.GetHostStatsCommand + cmd.ToString());
+ bool result = false;
+ string details = null;
+ object hostStats = null;
+
+ var entityType = "host";
+ ulong totalMemoryKBs;
+ ulong freeMemoryKBs;
+ double networkReadKBs;
+ double networkWriteKBs;
+ double cpuUtilization;
+
+ try
+ {
+ long hostId = (long)cmd.hostId;
+ wmiCallsV2.GetMemoryResources(out totalMemoryKBs, out freeMemoryKBs);
+ wmiCallsV2.GetProcessorUsageInfo(out cpuUtilization);
+
+ // TODO: can we assume that the host has only one adaptor?
+ string tmp;
+ var privateNic = GetNicInfoFromIpAddress(config.PrivateIpAddress, out tmp);
+ var nicStats = privateNic.GetIPv4Statistics(); //TODO: add IPV6 support, currentl
+ networkReadKBs = nicStats.BytesReceived;
+ networkWriteKBs = nicStats.BytesSent;
+
+ // Generate GetHostStatsAnswer
+ hostStats = new
+ {
+ hostId = hostId,
+ entityType = entityType,
+ cpuUtilization = cpuUtilization,
+ networkReadKBs = networkReadKBs,
+ networkWriteKBs = networkWriteKBs,
+ totalMemoryKBs = (double)totalMemoryKBs,
+ freeMemoryKBs = (double)freeMemoryKBs
+ };
+ result = true;
+ }
+ catch (Exception ex)
+ {
+ details = CloudStackTypes.GetHostStatsCommand + " failed on exception" + ex.Message;
+ logger.Error(details, ex);
+ }
+
+ object ansContent = new
+ {
+ result = result,
+ hostStats = hostStats,
+ details = details
+ };
+ return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetHostStatsAnswer);
+ }
+ }
+
+ // POST api/HypervResource/StartupCommand
+ [HttpPost]
+ [ActionName(CloudStackTypes.StartupCommand)]
+ public JContainer StartupCommand([FromBody]dynamic cmdArray)
+ {
+ using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+ {
+ logger.Info(cmdArray.ToString());
+ // Log agent configuration
+ logger.Info("Agent StartupRoutingCommand received " + cmdArray.ToString());
+ dynamic strtRouteCmd = cmdArray[0][CloudStackTypes.StartupRoutingCommand];
+
+ // Insert networking details
+ strtRouteCmd.privateIpAddress = config.PrivateIpAddress;
+ strtRouteCmd.privateNetmask = config.PrivateNetmask;
+ strtRouteCmd.privateMacAddress = config.PrivateMacAddress;
+ strtRouteCmd.storageIpAddress = config.PrivateIpAddress;
+ strtRouteCmd.storageNetmask = config.PrivateNetmask;
+ strtRouteCmd.storageMacAddress = config.PrivateMacAddress;
+ strtRouteCmd.gatewayIpAddress = config.GatewayIpAddress;
+ strtRouteCmd.caps = "hvm";
+
+ // Detect CPUs, speed, memory
+ uint cores;
+ uint mhz;
+ wmiCallsV2.GetProcessorResources(out cores, out mhz);
+ strtRouteCmd.cpus = cores;
+ strtRouteCmd.speed = mhz;
+ ulong memoryKBs;
+ ulong freeMemoryKBs;
+ wmiCallsV2.GetMemoryResources(out memoryKBs, out freeMemoryKBs);
+ strtRouteCmd.memory = memoryKBs * 1024; // Convert to bytes
+
+ // Need 2 Gig for DOM0, see http://technet.microsoft.com/en-us/magazine/hh750394.aspx
+ strtRouteCmd.dom0MinMemory = config.ParentPartitionMinMemoryMb * 1024 * 1024; // Convert to bytes
+
+ // Insert storage pool details.
+ //
+ // Read the localStoragePath for virtual disks from the Hyper-V configuration
+ // See http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/05/06/managing-the-default-virtual-machine-location-with-hyper-v.aspx
+ // for discussion of Hyper-V file locations paths.
+ string localStoragePath = wmiCallsV2.GetDefaultVirtualDiskFolder();
+ if (localStoragePath != null)
+ {
+ // GUID arbitrary. Host agents deals with storage pool in terms of localStoragePath.
+ // We use HOST guid.
+ string poolGuid = strtRouteCmd.guid;
+
+ if (poolGuid == null)
+ {
+ poolGuid = Guid.NewGuid().ToString();
+ logger.InfoFormat("Setting Startup StoragePool GUID to " + poolGuid);
+ }
+ else
+ {
+ logger.InfoFormat("Setting Startup StoragePool GUID same as HOST, i.e. " + poolGuid);
+ }
+
+ long capacity;
+ long available;
+ GetCapacityForLocalPath(localStoragePath, out capacity, out available);
+
+ logger.Debug(CloudStackTypes.StartupStorageCommand + " set available bytes to " + available);
+
+ string ipAddr = strtRouteCmd.privateIpAddress;
+ StoragePoolInfo pi = new StoragePoolInfo(
+ poolGuid.ToString(),
+ ipAddr,
+ localStoragePath,
+ localStoragePath,
+ StoragePoolType.Filesystem.ToString(),
+ capacity,
+ available);
+
+ // Build StartupStorageCommand using an anonymous type
+ // See http://stackoverflow.com/a/6029228/939250
+ object ansContent = new
+ {
+ poolInfo = pi,
+ guid = pi.uuid,
+ dataCenter = strtRouteCmd.dataCenter,
+ resourceType = StorageResourceType.STORAGE_POOL.ToString() // TODO: check encoding
+ };
+ JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.StartupStorageCommand, ansContent);
+ cmdArray.Add(ansObj);
+ }
+
+ // Convert result to array for type correctness?
+ logger.Info(CloudStackTypes.StartupCommand + " result is " + cmdArray.ToString());
+ return cmdArray;
+ }
+ }
+
+ public static System.Net.NetworkInformation.NetworkInterface GetNicInfoFromIpAddress(string ipAddress, out string subnet)
+ {
+ System.Net.NetworkInformation.NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
+ foreach (var nic in nics)
+ {
+ subnet = null;
+ // TODO: use to remove NETMASK and MAC from the config file, and to validate the IPAddress.
+ var nicProps = nic.GetIPProperties();
+ bool found = false;
+ foreach (var addr in nicProps.UnicastAddresses)
+ {
+ if (addr.Address.Equals(IPAddress.Parse(ipAddress)))
+ {
+ subnet = addr.IPv4Mask.ToString();
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ continue;
+ }
+ return nic;
+ }
+ throw new ArgumentException("No NIC for ipAddress " + ipAddress);
+ }
+
+ public static void GetCapacityForLocalPath(string localStoragePath, out long capacityBytes, out long availableBytes)
+ {
+ // NB: DriveInfo does not work for remote folders (http://stackoverflow.com/q/1799984/939250)
+ // DriveInfo requires a driver letter...
+ string fullPath = Path.GetFullPath(localStoragePath);
+ System.IO.DriveInfo poolInfo = new System.IO.DriveInfo(fullPath);
+ capacityBytes = poolInfo.TotalSize;
+ availableBytes = poolInfo.AvailableFreeSpace;
+
+ // Don't allow all of the Root Device to be used for virtual disks
+ if (poolInfo.RootDirectory.Name.ToLower().Equals(config.RootDeviceName))
+ {
+ availableBytes -= config.RootDeviceReservedSpaceBytes;
+ availableBytes = availableBytes > 0 ? availableBytes : 0;
+ capacityBytes -= config.RootDeviceReservedSpaceBytes;
+ capacityBytes = capacityBytes > 0 ? capacityBytes : 0;
+ }
+ }
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
new file mode 100644
index 00000000000..125c53ec384
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
@@ -0,0 +1,67 @@
+// 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.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2;
+using System.Management;
+
+namespace HypervResource
+{
+ public interface IWmiCallsV2
+ {
+ System.Management.ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType);
+ ComputerSystem AddUserData(ComputerSystem vm, string userData);
+ void AttachIso(string displayName, string iso);
+ void CreateDynamicVirtualHardDisk(ulong MaxInternalSize, string Path);
+ SyntheticEthernetPortSettingData CreateNICforVm(ComputerSystem vm, string mac);
+ ComputerSystem CreateVM(string name, long memory_mb, int vcpus);
+ void DeleteHostKvpItem(ComputerSystem vm, string key);
+ void DeleteSwitchPort(string elementName);
+ ComputerSystem DeployVirtualMachine(dynamic jsonObj, string systemVmIso);
+ void DestroyVm(dynamic jsonObj);
+ void DestroyVm(string displayName);
+ void DetachDisk(string displayName, string diskFileName);
+ ComputerSystem GetComputerSystem(string displayName);
+ string GetDefaultDataRoot();
+ string GetDefaultVirtualDiskFolder();
+ ResourceAllocationSettingData GetDvdDriveSettings(VirtualSystemSettingData vmSettings);
+ EthernetPortAllocationSettingData[] GetEthernetConnections(ComputerSystem vm);
+ SyntheticEthernetPortSettingData[] GetEthernetPortSettings(ComputerSystem vm);
+ ResourceAllocationSettingData GetIDEControllerSettings(VirtualSystemSettingData vmSettings, string cntrllerAddr);
+ ImageManagementService GetImageManagementService();
+ KvpExchangeComponentSettingData GetKvpSettings(VirtualSystemSettingData vmSettings);
+ void GetMemoryResources(out ulong physicalRamKBs, out ulong freeMemoryKBs);
+ MemorySettingData GetMemSettings(VirtualSystemSettingData vmSettings);
+ void GetProcessorResources(out uint cores, out uint mhz);
+ void GetProcessorUsageInfo(out double cpuUtilization);
+ ProcessorSettingData GetProcSettings(VirtualSystemSettingData vmSettings);
+ ResourceAllocationSettingData.ResourceAllocationSettingDataCollection GetResourceAllocationSettings(VirtualSystemSettingData vmSettings);
+ void GetSummaryInfo(System.Collections.Generic.Dictionary vmProcessorInfo, System.Collections.Generic.List vmsToInspect);
+ SyntheticEthernetPortSettingData GetSyntheticEthernetPortSettings(EthernetSwitchPort port);
+ VirtualSystemManagementService GetVirtualisationSystemManagementService();
+ VirtualEthernetSwitchManagementService GetVirtualSwitchManagementService();
+ EthernetSwitchPortVlanSettingData GetVlanSettings(EthernetPortAllocationSettingData ethernetConnection);
+ System.Collections.Generic.List GetVmElementNames();
+ VirtualSystemSettingData GetVmSettings(ComputerSystem vm);
+ void patchSystemVmIso(string vmName, string systemVmIso);
+ void SetState(ComputerSystem vm, ushort requiredState);
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Properties/AssemblyInfo.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000000..7bbc9b55adb
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Properties/AssemblyInfo.cs
@@ -0,0 +1,53 @@
+// 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.
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("HypervResource")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("HypervResource")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f1eb80c1-36fb-438c-a70d-0de5d5e295fb")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs
new file mode 100644
index 00000000000..e55f2ad1e99
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs
@@ -0,0 +1,131 @@
+// 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.
+using log4net;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HypervResource
+{
+ public class Utils
+ {
+ private static ILog s_logger = LogManager.GetLogger(typeof(Utils));
+
+ ///
+ /// Associate CloudStack object's content with a fully qualified type name.
+ ///
+ /// Fully qualified type name, e.g. "org.apache.cloudstack.storage.to.TemplateObjectTO"
+ /// Object's data, can be an anonymous object, e.g.
+ ///
+ public static JObject CreateCloudStackObject(string objType, object objValue)
+ {
+ JToken objContent = JToken.FromObject(objValue);
+ JProperty objTypeValuePairing = new JProperty(objType, objContent);
+
+ return new JObject(objTypeValuePairing);
+ }
+
+
+ ///
+ /// Copy file on network share to local volume.
+ ///
+ ///
+ /// Access to the network share is acheived by logging into the domain corresponding to the user credentials provided.
+ /// Windows impersonation does not suffice, because impersonation is limited to domains with an established trust relationship.
+ /// We have had to import Win32 API calls to allow login. There are a number of examples online. We follow the
+ /// one at http://stackoverflow.com/a/2541569/939250
+ ///
+ ///
+ ///
+ public static void DownloadCifsFileToLocalFile(string filePathRelativeToShare, NFSTO cifsShareDetails, string destFile)
+ {
+ try
+ {
+ IntPtr token = IntPtr.Zero;
+
+ bool isSuccess = LogonUser(cifsShareDetails.User, cifsShareDetails.Domain, cifsShareDetails.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref token);
+ using (WindowsImpersonationContext remoteIdentity = new WindowsIdentity(token).Impersonate())
+ {
+ String dest = "";
+ if (filePathRelativeToShare.EndsWith(".iso") || filePathRelativeToShare.EndsWith(".vhd") || filePathRelativeToShare.EndsWith(".vhdx"))
+ {
+ dest = Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare);
+ dest = dest.Replace('/', Path.DirectorySeparatorChar);
+ }
+ // if the filePathRelativeToShare string don't have filename and only a dir point then find the vhd files in that folder and use
+ // In the clean setup, first copy command wont be having the filename it contains onlyu dir path.
+ // we need to scan the folder point and then copy the file to destination.
+ else if (!filePathRelativeToShare.EndsWith(".vhd") || !filePathRelativeToShare.EndsWith(".vhdx"))
+ {
+ // scan the folder and get the vhd filename.
+ String uncPath = Path.Combine(cifsShareDetails.UncPath, Path.Combine(filePathRelativeToShare.Split('/')));
+ //uncPath = uncPath.Replace("/", "\\");
+ DirectoryInfo dir = new DirectoryInfo(uncPath);
+ FileInfo[] vhdFiles = dir.GetFiles("*.vhd*");
+ if (vhdFiles.Length > 0)
+ {
+ FileInfo file = vhdFiles[0];
+ dest = file.FullName;
+ }
+ }
+ s_logger.Info(CloudStackTypes.CopyCommand + ": copy " + Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare) + " to " + destFile);
+
+ File.Copy(dest, destFile, true);
+ remoteIdentity.Undo();
+ }
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ string errMsg = "Invalid user or password for the share " + cifsShareDetails.UncPath;
+ s_logger.Error(errMsg);
+
+ throw new ArgumentException(errMsg, ex);
+ }
+ }
+
+ // from http://stackoverflow.com/a/2541569/939250
+ #region imports
+ [DllImport("advapi32.dll", SetLastError = true)]
+ private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern bool CloseHandle(IntPtr handle);
+
+ [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public extern static bool DuplicateToken(IntPtr existingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr duplicateTokenHandle);
+ #endregion
+
+ #region logon consts
+ // logon types
+ const int LOGON32_LOGON_INTERACTIVE = 2;
+ const int LOGON32_LOGON_NETWORK = 3;
+ const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
+
+ // logon providers
+ const int LOGON32_PROVIDER_DEFAULT = 0;
+ const int LOGON32_PROVIDER_WINNT50 = 3;
+ const int LOGON32_PROVIDER_WINNT40 = 2;
+ const int LOGON32_PROVIDER_WINNT35 = 1;
+ #endregion
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
new file mode 100644
index 00000000000..18b96cc6ac1
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
@@ -0,0 +1,2120 @@
+// 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.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2;
+using log4net;
+using System.Globalization;
+using System.Management;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using CloudStack.Plugin.WmiWrappers.ROOT.CIMV2;
+using System.IO;
+using System.Net.NetworkInformation;
+using System.Net;
+
+namespace HypervResource
+{
+ public class WmiCallsV2 : IWmiCallsV2
+ {
+ public static String CloudStackUserDataKey = "cloudstack-vm-userdata";
+
+ public static void Initialize()
+ {
+ // Trigger assembly load into curren appdomain
+ }
+
+ private static ILog logger = LogManager.GetLogger(typeof(WmiCallsV2));
+
+ ///
+ /// Returns ping status of the given ip
+ ///
+ public static String PingHost(String ip)
+ {
+ return "Success";
+ }
+
+ ///
+ /// Returns ComputerSystem lacking any NICs and VOLUMEs
+ ///
+ public ComputerSystem AddUserData(ComputerSystem vm, string userData)
+ {
+ // Obtain controller for Hyper-V virtualisation subsystem
+ VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
+
+ // Create object to hold the data.
+ KvpExchangeDataItem kvpItem = KvpExchangeDataItem.CreateInstance();
+ kvpItem.LateBoundObject["Name"] = WmiCallsV2.CloudStackUserDataKey;
+ kvpItem.LateBoundObject["Data"] = userData;
+ kvpItem.LateBoundObject["Source"] = 0;
+ logger.Debug("VM " + vm.Name + " gets userdata " + userData);
+
+ // Update the resource settings for the VM.
+ System.Management.ManagementBaseObject kvpMgmtObj = kvpItem.LateBoundObject;
+ System.Management.ManagementPath jobPath;
+ String kvpStr = kvpMgmtObj.GetText(System.Management.TextFormat.CimDtd20);
+ uint ret_val = vmMgmtSvc.AddKvpItems(new String[] { kvpStr }, vm.Path, out jobPath);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
+ vm.ElementName,
+ vm.Name,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return vm;
+ }
+
+ ///
+ /// Returns ComputerSystem lacking any NICs and VOLUMEs
+ ///
+ public ComputerSystem CreateVM(string name, long memory_mb, int vcpus)
+ {
+ // Obtain controller for Hyper-V virtualisation subsystem
+ VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
+
+ // Create VM with correct name and default resources
+ ComputerSystem vm = CreateDefaultVm(vmMgmtSvc, name);
+
+ // Update the resource settings for the VM.
+
+ // Resource settings are referenced through the Msvm_VirtualSystemSettingData object.
+ VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+
+ // For memory settings, there is no Dynamic Memory, so reservation, limit and quantity are identical.
+ MemorySettingData memSettings = GetMemSettings(vmSettings);
+ memSettings.LateBoundObject["VirtualQuantity"] = memory_mb;
+ memSettings.LateBoundObject["Reservation"] = memory_mb;
+ memSettings.LateBoundObject["Limit"] = memory_mb;
+
+ // Update the processor settings for the VM, static assignment of 100% for CPU limit
+ ProcessorSettingData procSettings = GetProcSettings(vmSettings);
+ procSettings.LateBoundObject["VirtualQuantity"] = vcpus;
+ procSettings.LateBoundObject["Reservation"] = vcpus;
+ procSettings.LateBoundObject["Limit"] = 100000;
+
+ ModifyVmResources(vmMgmtSvc, vm, new String[] {
+ memSettings.LateBoundObject.GetText(TextFormat.CimDtd20),
+ procSettings.LateBoundObject.GetText(TextFormat.CimDtd20)
+ });
+ logger.InfoFormat("VM with display name {0} has GUID {1}", vm.ElementName, vm.Name);
+ logger.DebugFormat("Resources for vm {0}: {1} MB memory, {2} vcpus", name, memory_mb, vcpus);
+
+ return vm;
+ }
+
+ ///
+ /// Create a (synthetic) nic, and attach it to the vm
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SyntheticEthernetPortSettingData CreateNICforVm(ComputerSystem vm, string mac)
+ {
+ logger.DebugFormat("Creating nic for VM {0} (GUID {1})", vm.ElementName, vm.Name);
+
+ // Obtain controller for Hyper-V networking subsystem
+ var vmNetMgmtSvc = GetVirtualSwitchManagementService();
+
+ // Create NIC resource by cloning the default NIC
+ var synthNICsSettings = SyntheticEthernetPortSettingData.GetInstances(vmNetMgmtSvc.Scope, "InstanceID LIKE \"%Default\"");
+
+ // Assert
+ if (synthNICsSettings.Count != 1)
+ {
+ var errMsg = string.Format("Internal error, coudl not find default SyntheticEthernetPort instance");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ var defaultSynthNICSettings = synthNICsSettings.OfType().First();
+
+ var newSynthNICSettings = new SyntheticEthernetPortSettingData((ManagementBaseObject)defaultSynthNICSettings.LateBoundObject.Clone());
+
+ // Assign configuration to new NIC
+ string normalisedMAC = string.Join("", (mac.Split(new char[] { ':' })));
+ newSynthNICSettings.LateBoundObject["ElementName"] = vm.ElementName;
+ newSynthNICSettings.LateBoundObject["Address"] = normalisedMAC;
+ newSynthNICSettings.LateBoundObject["StaticMacAddress"] = "TRUE";
+ newSynthNICSettings.LateBoundObject["VirtualSystemIdentifiers"] = new string[] { "{" + Guid.NewGuid().ToString() + "}" };
+ newSynthNICSettings.CommitObject();
+
+ // Insert NIC into vm
+ string[] newResources = new string[] { newSynthNICSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20)};
+ ManagementPath[] newResourcePaths = AddVirtualResource(newResources, vm );
+
+ // assert
+ if (newResourcePaths.Length != 1)
+ {
+ var errMsg = string.Format(
+ "Failed to properly insert a single NIC on VM {0} (GUID {1}): number of resource created {2}",
+ vm.ElementName,
+ vm.Name,
+ newResourcePaths.Length);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return new SyntheticEthernetPortSettingData(newResourcePaths[0]);
+ }
+
+ public const string IDE_HARDDISK_CONTROLLER = "Microsoft:Hyper-V:Emulated IDE Controller";
+ public const string SCSI_CONTROLLER = "Microsoft:Hyper-V:Synthetic SCSI Controller";
+ public const string IDE_HARDDISK_DRIVE = "Microsoft:Hyper-V:Synthetic Disk Drive";
+ public const string IDE_ISO_DRIVE = "Microsoft:Hyper-V:Synthetic DVD Drive";
+
+ // TODO: names harvested from Msvm_ResourcePool, not clear how to create new instances
+ public const string IDE_ISO_DISK = "Microsoft:Hyper-V:Virtual CD/DVD Disk"; // For IDE_ISO_DRIVE
+ public const string IDE_HARDDISK_DISK = "Microsoft:Hyper-V:Virtual Hard Disk"; // For IDE_HARDDISK_DRIVE
+
+ ///
+ /// Create new VM. By default we start it.
+ ///
+ public ComputerSystem DeployVirtualMachine(dynamic jsonObj, string systemVmIso)
+ {
+ var vmInfo = jsonObj.vm;
+ string vmName = vmInfo.name;
+ var nicInfo = vmInfo.nics;
+ int vcpus = vmInfo.cpus;
+ int memSize = vmInfo.maxRam / 1048576;
+ string errMsg = vmName;
+ var diskDrives = vmInfo.disks;
+ var bootArgs = vmInfo.bootArgs;
+
+ // assert
+ errMsg = vmName + ": missing disk information, array empty or missing, agent expects *at least* one disk for a VM";
+ if (diskDrives == null)
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+ // assert
+ errMsg = vmName + ": missing NIC information, array empty or missing, agent expects at least an empty array.";
+ if (nicInfo == null )
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+
+
+ // For existing VMs, return when we spot one of this name not stopped. In the meantime, remove any existing VMs of same name.
+ ComputerSystem vmWmiObj = null;
+ while ((vmWmiObj = GetComputerSystem(vmName)) != null)
+ {
+ logger.WarnFormat("Create request for existing vm, name {0}", vmName);
+ if (vmWmiObj.EnabledState == EnabledState.Disabled)
+ {
+ logger.InfoFormat("Deleting existing VM with name {0}, before we go on to create a VM with the same name", vmName);
+ DestroyVm(vmName);
+ }
+ else if (vmWmiObj.EnabledState == EnabledState.Enabled)
+ {
+ string infoMsg = string.Format("Create VM discovered there exists a VM with name {0}, state {1}",
+ vmName,
+ EnabledState.ToString(vmWmiObj.EnabledState));
+ logger.Info(infoMsg);
+ return vmWmiObj;
+ }
+ else
+ {
+ errMsg = string.Format("Create VM failing, because there exists a VM with name {0}, state {1}",
+ vmName,
+ EnabledState.ToString(vmWmiObj.EnabledState));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ // Create vm carcase
+ logger.DebugFormat("Going ahead with create VM {0}, {1} vcpus, {2}MB RAM", vmName, vcpus, memSize);
+ var newVm = CreateVM(vmName, memSize, vcpus);
+
+ // Add a SCSI controller for attaching/detaching data volumes.
+ AddScsiControllerToVm(newVm);
+
+ foreach (var diskDrive in diskDrives)
+ {
+ string vhdFile = null;
+ string diskName = null;
+ VolumeObjectTO volInfo = VolumeObjectTO.ParseJson(diskDrive.data);
+ if (volInfo != null)
+ {
+ // assert
+ errMsg = vmName + ": volume missing primaryDataStore for disk " + diskDrive.ToString();
+ if (volInfo.primaryDataStore == null)
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+ diskName = volInfo.name;
+
+ // assert
+ errMsg = vmName + ": can't deal with DataStore type for disk " + diskDrive.ToString();
+ if (volInfo.primaryDataStore == null)
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+ errMsg = vmName + ": Malformed PrimaryDataStore for disk " + diskDrive.ToString();
+ if (String.IsNullOrEmpty(volInfo.primaryDataStore.path))
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+ errMsg = vmName + ": Missing folder PrimaryDataStore for disk " + diskDrive.ToString() + ", missing path: " + volInfo.primaryDataStore.path;
+ if (!Directory.Exists(volInfo.primaryDataStore.path))
+ {
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+
+ vhdFile = volInfo.FullFileName;
+ if (!System.IO.File.Exists(vhdFile))
+ {
+ errMsg = vmName + ": non-existent volume, missing " + vhdFile + " for drive " + diskDrive.ToString();
+ logger.Error(errMsg);
+ throw new ArgumentException(errMsg);
+ }
+ logger.Debug("Going to create " + vmName + " with attached voluem " + diskName + " at " + vhdFile);
+ }
+
+ string driveType = diskDrive.type;
+
+ string ideCtrllr = "0";
+ string driveResourceType = null;
+ switch (driveType) {
+ case "ROOT":
+ ideCtrllr = "0";
+ driveResourceType = IDE_HARDDISK_DRIVE;
+ break;
+ case "ISO":
+ ideCtrllr = "1";
+ driveResourceType = IDE_ISO_DRIVE;
+ break;
+ default:
+ // TODO: double check exception type
+ errMsg = string.Format("Unknown disk type {0} for disk {1}, vm named {2}",
+ string.IsNullOrEmpty(driveType) ? "NULL" : driveType,
+ string.IsNullOrEmpty(diskName) ? "NULL" : diskName, vmName);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ logger.DebugFormat("Create disk type {1} (Named: {0}), on vm {2} {3}", diskName, driveResourceType, vmName,
+ string.IsNullOrEmpty(vhdFile) ? " no disk to insert" : ", inserting disk" +vhdFile );
+ AddDiskDriveToVm(newVm, vhdFile, ideCtrllr, driveResourceType);
+ }
+
+ String publicIpAddress = "";
+ // Add the Nics to the VM in the deviceId order.
+ for (int i = 0; i <= 2; i++)
+ {
+ foreach (var nic in nicInfo)
+ {
+
+ int nicid = nic.deviceId;
+ string mac = nic.mac;
+ string vlan = null;
+ string isolationUri = nic.isolationUri;
+ if (isolationUri != null && isolationUri.StartsWith("vlan://") && !isolationUri.Equals("vlan://untagged"))
+ {
+ vlan = isolationUri.Substring("vlan://".Length);
+ int tmp;
+ if (!int.TryParse(vlan, out tmp))
+ {
+ // TODO: double check exception type
+ errMsg = string.Format("Invalid VLAN value {0} for on vm {1} for nic uuid {2}", isolationUri, vmName, nic.uuid);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ if (i == 2)
+ {
+ publicIpAddress = nic.ip;
+ }
+
+ if (nicid == i)
+ {
+ // Create network adapter
+ var newAdapter = CreateNICforVm(newVm, mac);
+
+ // connection to vswitch
+ var portSettings = AttachNicToPort(newVm, newAdapter);
+
+ // set vlan
+ if (vlan != null)
+ {
+ SetPortVlan(vlan, portSettings);
+ }
+
+ logger.DebugFormat("Created adapter {0} on port {1}, {2}",
+ newAdapter.Path, portSettings.Path, (vlan == null ? "No VLAN" : "VLAN " + vlan));
+ }
+ }
+ }
+
+ // pass the boot args for the VM using KVP component.
+ // We need to pass the boot args to system vm's to get them configured with cloudstack configuration.
+ // Add new user data
+ var vm = GetComputerSystem(vmName);
+ if (bootArgs != null && !String.IsNullOrEmpty((string)bootArgs))
+ {
+
+ String bootargs = bootArgs;
+ AddUserData(vm, bootargs);
+
+
+ // Get existing KVP
+ //var vmSettings = GetVmSettings(vm);
+ //var kvpInfo = GetKvpSettings(vmSettings);
+ //logger.DebugFormat("Boot Args presisted on the VM are ", kvpInfo);
+ //AddUserData(vm, bootargs);
+
+ // Verify key added to subsystem
+ //kvpInfo = GetKvpSettings(vmSettings);
+
+ // HostExchangesItems are embedded objects in the sense that the object value is stored and not a reference to the object.
+ //kvpProps = kvpInfo.HostExchangeItems;
+
+ }
+ // call patch systemvm iso only for systemvms
+ if (vmName.StartsWith("r-") || vmName.StartsWith("s-") || vmName.StartsWith("v-"))
+ {
+ patchSystemVmIso(vmName, systemVmIso);
+ }
+
+ logger.DebugFormat("Starting VM {0}", vmName);
+ SetState(newVm, RequiredState.Enabled);
+
+ // we need to reboot to get the hv kvp daemon get started vr gets configured.
+ if (vmName.StartsWith("r-") || vmName.StartsWith("s-") || vmName.StartsWith("v-"))
+ {
+ System.Threading.Thread.Sleep(90000);
+ SetState(newVm, RequiredState.Reset);
+ // wait for the second boot and then return with sucesss
+ pingResource(publicIpAddress);
+ }
+ logger.InfoFormat("Started VM {0}", vmName);
+ return newVm;
+ }
+
+ public static Boolean pingResource(String ip)
+ {
+ PingOptions pingOptions = null;
+ PingReply pingReply = null;
+ IPAddress ipAddress = null;
+ Ping pingSender = new Ping();
+ int numberOfPings = 4;
+ int pingTimeout = 1000;
+ int byteSize = 32;
+ byte[] buffer = new byte[byteSize];
+ ipAddress = IPAddress.Parse(ip);
+ pingOptions = new PingOptions();
+ for (int i = 0; i < numberOfPings; i++)
+ {
+ pingReply = pingSender.Send(ipAddress, pingTimeout, buffer, pingOptions);
+ if (pingReply.Status == IPStatus.Success)
+ {
+ return true;
+ }
+ else
+ {
+ // wait for the second boot and then return with suces
+ System.Threading.Thread.Sleep(30000);
+ }
+ }
+ return false;
+ }
+
+ private EthernetPortAllocationSettingData AttachNicToPort(ComputerSystem newVm, SyntheticEthernetPortSettingData newAdapter)
+ {
+ // Get the virtual switch
+ VirtualEthernetSwitch vSwitch = GetExternalVirtSwitch();
+
+ // Create port for adapter
+ var defaultEthernetPortSettings = EthernetPortAllocationSettingData.GetInstances(vSwitch.Scope, "InstanceID LIKE \"%Default\"");
+
+ // assert
+ if (defaultEthernetPortSettings.Count != 1)
+ {
+ var errMsg = string.Format("Internal error, coudl not find default EthernetPortAllocationSettingData instance");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ var defaultEthernetPortSettingsObj = defaultEthernetPortSettings.OfType().First();
+ var newEthernetPortSettings = new EthernetPortAllocationSettingData((ManagementBaseObject)defaultEthernetPortSettingsObj.LateBoundObject.Clone());
+ newEthernetPortSettings.LateBoundObject["Parent"] = newAdapter.Path.Path;
+ newEthernetPortSettings.LateBoundObject["HostResource"] = new string[] { vSwitch.Path.Path };
+
+ // Insert NIC into vm
+ string[] newResources = new string[] { newEthernetPortSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
+ ManagementPath[] newResourcePaths = AddVirtualResource(newResources, newVm);
+
+ // assert
+ if (newResourcePaths.Length != 1)
+ {
+ var errMsg = string.Format(
+ "Failed to properly insert a single NIC on VM {0} (GUID {1}): number of resource created {2}",
+ newVm.ElementName,
+ newVm.Name,
+ newResourcePaths.Length);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return new EthernetPortAllocationSettingData(newResourcePaths[0]);
+ }
+
+ /// this method is to add a dvd drive and attach the systemvm iso.
+ ///
+ public void patchSystemVmIso(String vmName, String systemVmIso)
+ {
+ ComputerSystem vmObject = GetComputerSystem(vmName);
+ AddDiskDriveToVm(vmObject, "", "1", IDE_ISO_DRIVE);
+ AttachIso(vmName, systemVmIso);
+ }
+
+
+ ///
+ ///
+ ///
+ /// IDE_HARDDISK_DRIVE or IDE_ISO_DRIVE
+ public ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType)
+ {
+ logger.DebugFormat("Creating DISK for VM {0} (GUID {1}) by attaching {2}",
+ vm.ElementName,
+ vm.Name,
+ vhdfile);
+
+ // Determine disk type for drive and assert drive type valid
+ string diskResourceSubType = null;
+ switch(driveResourceType) {
+ case IDE_HARDDISK_DRIVE:
+ diskResourceSubType = IDE_HARDDISK_DISK;
+ break;
+ case IDE_ISO_DRIVE:
+ diskResourceSubType = IDE_ISO_DISK;
+ break;
+ default:
+ var errMsg = string.Format(
+ "Unrecognised disk drive type {0} for VM {1} (GUID {2})",
+ string.IsNullOrEmpty(driveResourceType) ? "NULL": driveResourceType,
+ vm.ElementName,
+ vm.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ManagementPath newDrivePath = AttachNewDriveToVm(vm, cntrllerAddr, driveResourceType);
+
+ // If there's not disk to insert, we are done.
+ if (String.IsNullOrEmpty(vhdfile))
+ {
+ logger.DebugFormat("No disk to be added to drive, disk drive {0} is complete", newDrivePath.Path);
+ }
+ else
+ {
+ InsertDiskImage(vm, vhdfile, diskResourceSubType, newDrivePath);
+ }
+ return newDrivePath;
+ }
+
+
+ public void DetachDisk(string displayName, string diskFileName)
+ {
+ logger.DebugFormat("Got request to detach virtual disk {0} from vm {1}", diskFileName, displayName);
+
+ ComputerSystem vm = GetComputerSystem(displayName);
+ if (vm == null)
+ {
+ logger.DebugFormat("VM {0} not found", displayName);
+ return;
+ }
+ else
+ {
+ RemoveStorageImageFromVm(vm, diskFileName);
+ }
+ }
+
+ ///
+ /// Removes a disk image from a drive, but does not remove the drive itself.
+ ///
+ ///
+ ///
+ private void RemoveStorageImageFromVm(ComputerSystem vm, string diskFileName)
+ {
+ // Obtain StorageAllocationSettingData for disk
+ StorageAllocationSettingData.StorageAllocationSettingDataCollection storageSettingsObjs = StorageAllocationSettingData.GetInstances();
+
+ StorageAllocationSettingData imageToRemove = null;
+ foreach (StorageAllocationSettingData item in storageSettingsObjs)
+ {
+ if (item.HostResource == null || item.HostResource.Length != 1)
+ {
+ continue;
+ }
+
+ string hostResource = item.HostResource[0];
+ if (!hostResource.Equals(diskFileName))
+ {
+ continue;
+ }
+ imageToRemove = item;
+ }
+
+ // assert
+ if (imageToRemove == null)
+ {
+ var errMsg = string.Format(
+ "Failed to remove disk image {0} from VM {1} (GUID {2}): the disk image is not attached.",
+ diskFileName,
+ vm.ElementName,
+ vm.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ RemoveStorageResource(imageToRemove.Path, vm);
+
+ logger.InfoFormat("REmoved disk image {0} from VM {1} (GUID {2}): the disk image is not attached.",
+ diskFileName,
+ vm.ElementName,
+ vm.Name);
+ }
+
+ private ManagementPath AttachNewDriveToVm(ComputerSystem vm, string cntrllerAddr, string driveType)
+ {
+ // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it.
+ VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+ var ctrller = GetIDEControllerSettings(vmSettings, cntrllerAddr);
+
+ // A description of the drive is created by modifying a clone of the default ResourceAllocationSettingData for that drive type
+ string defaultDriveQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", driveType);
+ var newDiskDriveSettings = CloneResourceAllocationSetting(defaultDriveQuery);
+
+ // Set IDE controller and address on the controller for the new drive
+ newDiskDriveSettings.LateBoundObject["Parent"] = ctrller.Path.ToString();
+ newDiskDriveSettings.LateBoundObject["AddressOnParent"] = "0";
+ newDiskDriveSettings.CommitObject();
+
+ // Add this new disk drive to the VM
+ logger.DebugFormat("Creating disk drive type {0}, parent IDE controller is {1} and address on controller is {2}",
+ newDiskDriveSettings.ResourceSubType,
+ newDiskDriveSettings.Parent,
+ newDiskDriveSettings.AddressOnParent);
+ string[] newDriveResource = new string[] { newDiskDriveSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
+ ManagementPath[] newDrivePaths = AddVirtualResource(newDriveResource, vm);
+
+ // assert
+ if (newDrivePaths.Length != 1)
+ {
+ var errMsg = string.Format(
+ "Failed to add disk drive type {3} to VM {0} (GUID {1}): number of resource created {2}",
+ vm.ElementName,
+ vm.Name,
+ newDrivePaths.Length,
+ driveType);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ logger.DebugFormat("New disk drive type {0} WMI path is {1}s",
+ newDiskDriveSettings.ResourceSubType,
+ newDrivePaths[0].Path);
+ return newDrivePaths[0];
+ }
+
+ private ManagementPath AddScsiControllerToVm(ComputerSystem vm)
+ {
+ // A description of the controller is created by modifying a clone of the default ResourceAllocationSettingData for scsi controller
+ string scsiQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", SCSI_CONTROLLER);
+ var scsiSettings = CloneResourceAllocationSetting(scsiQuery);
+
+ scsiSettings.LateBoundObject["ElementName"] = "SCSI Controller";
+ scsiSettings.CommitObject();
+
+ // Insert SCSI controller into vm
+ string[] newResources = new string[] { scsiSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
+ ManagementPath[] newResourcePaths = AddVirtualResource(newResources, vm);
+
+ // assert
+ if (newResourcePaths.Length != 1)
+ {
+ var errMsg = string.Format(
+ "Failed to add scsi controller to VM {0} (GUID {1}): number of resource created {2}",
+ vm.ElementName,
+ vm.Name,
+ newResourcePaths.Length);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ logger.DebugFormat("New controller type {0} WMI path is {1}s",
+ scsiSettings.ResourceSubType,
+ newResourcePaths[0].Path);
+ return newResourcePaths[0];
+ }
+
+
+ private void InsertDiskImage(ComputerSystem vm, string diskImagePath, string diskResourceSubType, ManagementPath drivePath)
+ {
+ // A description of the disk is created by modifying a clone of the default ResourceAllocationSettingData for that disk type
+ string defaultDiskQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", diskResourceSubType);
+ var newDiskSettings = CloneStorageAllocationSetting(defaultDiskQuery);
+
+ // Set file containing the disk image
+ newDiskSettings.LateBoundObject["Parent"] = drivePath.Path;
+
+ // V2 API uses HostResource to specify image, see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx
+ newDiskSettings.LateBoundObject["HostResource"] = new string[] { diskImagePath };
+ newDiskSettings.CommitObject();
+
+ // Add the new Msvm_StorageAllocationSettingData object as a virtual hard disk to the vm.
+ string[] newDiskResource = new string[] { newDiskSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
+ ManagementPath[] newDiskPaths = AddStorageResource(newDiskResource, vm);
+ // assert
+ if (newDiskPaths.Length != 1)
+ {
+ var errMsg = string.Format(
+ "Failed to add disk image type {3} to VM {0} (GUID {1}): number of resource created {2}",
+ vm.ElementName,
+ vm.Name,
+ newDiskPaths.Length,
+ diskResourceSubType);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ logger.InfoFormat("Created disk {2} for VM {0} (GUID {1}), image {3} ",
+ vm.ElementName,
+ vm.Name,
+ newDiskPaths[0].Path,
+ diskImagePath);
+ }
+
+ ///
+ /// Create Msvm_StorageAllocationSettingData corresponding to the ISO image, and
+ /// associate this with the VM's DVD drive.
+ ///
+ private void AttachIsoToVm(ComputerSystem vm, string isoPath)
+ {
+ // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it.
+ VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+ var driveWmiObj = GetDvdDriveSettings(vmSettings);
+
+ InsertDiskImage(vm, isoPath, IDE_ISO_DISK, driveWmiObj.Path);
+ }
+
+
+
+ private static ResourceAllocationSettingData CloneResourceAllocationSetting(string wmiQuery)
+ {
+ var defaultDiskDriveSettingsObjs = ResourceAllocationSettingData.GetInstances(wmiQuery);
+
+ // assert
+ if (defaultDiskDriveSettingsObjs.Count != 1)
+ {
+ var errMsg = string.Format("Failed to find Msvm_ResourceAllocationSettingData for the query {0}", wmiQuery);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ResourceAllocationSettingData defaultDiskDriveSettings = defaultDiskDriveSettingsObjs.OfType().First();
+ return new ResourceAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone());
+ }
+
+ public void AttachIso(string displayName, string iso)
+ {
+ logger.DebugFormat("Got request to attach iso {0} to vm {1}", iso, displayName);
+
+ ComputerSystem vm = GetComputerSystem(displayName);
+ if (vm == null)
+ {
+ logger.DebugFormat("VM {0} not found", displayName);
+ return;
+ }
+ else
+ {
+ AttachIsoToVm(vm, iso);
+ }
+ }
+
+ public void DestroyVm(dynamic jsonObj)
+ {
+ string vmToDestroy = jsonObj.vmName;
+ DestroyVm(vmToDestroy);
+ }
+
+ ///
+ /// Remove all VMs and all SwitchPorts with the displayName. VHD gets deleted elsewhere.
+ ///
+ ///
+ public void DestroyVm(string displayName)
+ {
+ logger.DebugFormat("Got request to destroy vm {0}", displayName);
+
+ var vm = GetComputerSystem(displayName);
+ if ( vm == null )
+ {
+ logger.DebugFormat("VM {0} already destroyed (or never existed)", displayName);
+ return;
+ }
+
+ // Stop VM
+ logger.DebugFormat("Stop VM {0} (GUID {1})", vm.ElementName, vm.Name);
+ SetState(vm, RequiredState.Disabled);
+
+ // Delete SwitchPort
+ logger.DebugFormat("Remove associated switch ports for VM {0} (GUID {1})", vm.ElementName, vm.Name);
+ DeleteSwitchPort(vm.ElementName);
+
+ // Delete VM
+ var virtSysMgmtSvc = GetVirtualisationSystemManagementService();
+ ManagementPath jobPath;
+
+ do
+ {
+ logger.DebugFormat("Delete VM {0} (GUID {1})", vm.ElementName, vm.Name);
+ var ret_val = virtSysMgmtSvc.DestroySystem(vm.Path, out jobPath);
+
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed Delete VM {0} (GUID {1}) due to {2}",
+ vm.ElementName,
+ vm.Name,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ vm = GetComputerSystem(displayName);
+ }
+ while (vm != null);
+ }
+
+ ///
+ /// Create new storage media resources, e.g. hard disk images and ISO disk images
+ /// see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx
+ ///
+ ///
+ ///
+ private static StorageAllocationSettingData CloneStorageAllocationSetting(string wmiQuery)
+ {
+ var defaultDiskImageSettingsObjs = StorageAllocationSettingData.GetInstances(wmiQuery);
+
+ // assert
+ if (defaultDiskImageSettingsObjs.Count != 1)
+ {
+ var errMsg = string.Format("Failed to find Msvm_StorageAllocationSettingData for the query {0}", wmiQuery);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ StorageAllocationSettingData defaultDiskDriveSettings = defaultDiskImageSettingsObjs.OfType().First();
+ return new StorageAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone());
+ }
+
+ /// < summary>
+ /// Removes a storage resource from a computer system.
+ ///
+ /// Path that uniquely identifies the resource.
+ /// VM to which the disk image will be attached.
+ // Add new
+ private void RemoveNetworkResource(ManagementPath resourcePath)
+ {
+ var virtSwitchMgmtSvc = GetVirtualSwitchManagementService();
+ ManagementPath jobPath;
+ var ret_val = virtSwitchMgmtSvc.RemoveResourceSettings(
+ new ManagementPath[] { resourcePath },
+ out jobPath);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to remove network resources {0} from switch due to {1}",
+ resourcePath.Path,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ /// < summary>
+ /// Removes a storage resource from a computer system.
+ ///
+ /// Path that uniquely identifies the resource.
+ /// VM to which the disk image will be attached.
+ private void RemoveStorageResource(ManagementPath resourcePath, ComputerSystem vm)
+ {
+ var virtSysMgmtSvc = GetVirtualisationSystemManagementService();
+
+ ManagementPath jobPath;
+ var ret_val = virtSysMgmtSvc.RemoveResourceSettings(
+ new ManagementPath[] { resourcePath },
+ out jobPath);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to remove resource {0} from VM {1} (GUID {2}) due to {3}",
+ resourcePath.Path,
+ vm.ElementName,
+ vm.Name,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ public void SetState(ComputerSystem vm, ushort requiredState)
+ {
+ logger.InfoFormat(
+ "Changing state of {0} (GUID {1}) to {2}",
+ vm.ElementName,
+ vm.Name,
+ RequiredState.ToString(requiredState));
+
+ ManagementPath jobPath;
+ // DateTime is unused
+ var ret_val = vm.RequestStateChange(requiredState, new DateTime(), out jobPath);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val == 32775)
+ { // TODO: check
+ logger.InfoFormat("RequestStateChange returned 32775, which means vm in wrong state for requested state change. Treating as if requested state was reached");
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to change state of VM {0} (GUID {1}) to {2} due to {3}",
+ vm.ElementName,
+ vm.Name,
+ RequiredState.ToString(requiredState),
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ logger.InfoFormat(
+ "Successfully changed vm state of {0} (GUID {1} to requested state {2}",
+ vm.ElementName,
+ vm.Name,
+ requiredState);
+ }
+
+
+ //TODO: Write method to delete SwitchPort based on Name
+ ///
+ /// Delete switch port by removing settings from the switch
+ ///
+ ///
+ ///
+ public void DeleteSwitchPort(string elementName)
+ {
+ // Get NIC path
+ var condition = string.Format("ElementName=\"{0}\"", elementName);
+ var virtSwitchMgmtSvc = GetVirtualSwitchManagementService();
+
+ var switchPortCollection = EthernetSwitchPort.GetInstances(virtSwitchMgmtSvc.Scope, condition);
+ if (switchPortCollection.Count == 0)
+ {
+ return;
+ }
+
+ foreach (EthernetSwitchPort port in switchPortCollection)
+ {
+ var settings = GetSyntheticEthernetPortSettings(port);
+ RemoveNetworkResource(settings.Path);
+ }
+ }
+
+ public SyntheticEthernetPortSettingData GetSyntheticEthernetPortSettings(EthernetSwitchPort port)
+ {
+ // An ASSOCIATOR object provides the cross reference from the EthernetSwitchPort and the
+ // SyntheticEthernetPortSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(port.Path.Path, SyntheticEthernetPortSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(port.Scope, wmiObjQuery);
+ var wmiObjCollection = new SyntheticEthernetPortSettingData.SyntheticEthernetPortSettingDataCollection(wmiObjectSearch.Get());
+
+ // When snapshots are taken into account, there can be multiple settings objects
+ // take the first one that isn't a snapshot
+ foreach (SyntheticEthernetPortSettingData wmiObj in wmiObjCollection)
+ {
+ return wmiObj;
+ }
+
+ var errMsg = string.Format("No SyntheticEthernetPortSettingData for port {0}, path {1}", port.ElementName, port.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ///
+ /// Adds storage images to coputer system (disk image, iso image).
+ ///
+ /// Msvm_StorageAllocationSettings with HostResource configured with image
+ /// file and Parent set to a controller associated with the ComputerSystem
+ /// VM to which the disk image will be attached.
+ // Add new
+ private ManagementPath[] AddStorageResource(string[] storageSettings, ComputerSystem vm)
+ {
+ return AddVirtualResource(storageSettings, vm);
+ }
+
+ private ManagementPath[] AddVirtualResource(string[] resourceSettings, ComputerSystem vm )
+ {
+ var virtSysMgmtSvc = GetVirtualisationSystemManagementService();
+
+ ManagementPath jobPath;
+ ManagementPath[] resourcePaths;
+ var ret_val = virtSysMgmtSvc.AddResourceSettings(
+ vm.Path,
+ resourceSettings,
+ out jobPath,
+ out resourcePaths);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to add resources to VM {0} (GUID {1}) due to {2}",
+ vm.ElementName,
+ vm.Name,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return resourcePaths;
+ }
+
+ private ManagementPath[] AddFeatureSettings(string[] featureSettings, ManagementPath affectedConfiguration)
+ {
+ var virtSysMgmtSvc = GetVirtualisationSystemManagementService();
+
+ ManagementPath jobPath;
+ ManagementPath[] resultSettings;
+ var ret_val = virtSysMgmtSvc.AddFeatureSettings(
+ affectedConfiguration,
+ featureSettings,
+ out jobPath,
+ out resultSettings);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to add features settings {0} to resource {1} due to {2}",
+ featureSettings,
+ affectedConfiguration,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return resultSettings;
+ }
+
+ private ManagementPath SetPortVlan(string vlan, EthernetPortAllocationSettingData portPath)
+ {
+ logger.DebugFormat("Setting VLAN to {0}", vlan);
+
+ var vmVirtMgmtSvc = GetVirtualisationSystemManagementService();
+ EthernetSwitchPortVlanSettingData.GetInstances();
+
+ // Create NIC resource by cloning the default NIC
+ var vlanSettings = EthernetSwitchPortVlanSettingData.GetInstances(vmVirtMgmtSvc.Scope, "InstanceID LIKE \"%Default\"");
+
+ // Assert
+ if (vlanSettings.Count != 1)
+ {
+ var errMsg = string.Format("Internal error, could not find default EthernetSwitchPortVlanSettingData instance");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ var defaultVlanSettings = vlanSettings.OfType().First();
+
+ var newVlanSettings = new EthernetSwitchPortVlanSettingData((ManagementBaseObject)defaultVlanSettings.LateBoundObject.Clone());
+
+ // Assign configuration to new NIC
+ newVlanSettings.LateBoundObject["AccessVlanId"] = vlan;
+ newVlanSettings.LateBoundObject["OperationMode"] = 1; // Access=1, trunk=2, private=3 ;
+ newVlanSettings.CommitObject();
+
+ // Insert NIC into vm
+ string[] newResources = new string[] { newVlanSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
+ ManagementPath[] newResourcePaths = AddFeatureSettings(newResources, portPath.Path);
+
+ // assert
+ if (newResourcePaths.Length != 1)
+ {
+ var errMsg = string.Format(
+ "Failed to properly set VLAN to {0} for NIC on port {1}",
+ vlan,
+ portPath.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return newResourcePaths[0];
+ }
+
+
+ ///
+ /// External VSwitch has an external NIC, and we assume there is only one external NIC and one external vswitch.
+ ///
+ ///
+ ///
+ /// Throws if there is no vswitch
+ ///
+ /// With V1 API, external ethernet port was attached to the land endpoint, which was attached to the switch.
+ /// e.g. Msvm_ExternalEthernetPort -> SwitchLANEndpoint -> SwitchPort -> VirtualSwitch
+ ///
+ /// With V2 API, there are two kinds of lan endpoint: one on the computer system and one on the switch
+ /// e.g. Msvm_ExternalEthernetPort -> LANEndpoint -> LANEdnpoint -> EthernetSwitchPort -> VirtualEthernetSwitch
+ ///
+ public static VirtualEthernetSwitch GetExternalVirtSwitch()
+ {
+ // Work back from the first *bound* external NIC we find.
+ var externNICs = ExternalEthernetPort.GetInstances("IsBound = TRUE");
+
+ // Assert
+ if (externNICs.Count == 0 )
+ {
+ var errMsg = "No ExternalEthernetPort available to Hyper-V";
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ExternalEthernetPort externNIC = externNICs.OfType().First();
+ // A sequence of ASSOCIATOR objects need to be traversed to get from external NIC the vswitch.
+ // We use ManagementObjectSearcher objects to execute this sequence of questions
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var endpointQuery = new RelatedObjectQuery(externNIC.Path.Path, LANEndpoint.CreatedClassName);
+ var endpointSearch = new ManagementObjectSearcher(externNIC.Scope, endpointQuery);
+ var endpointCollection = new LANEndpoint.LANEndpointCollection(endpointSearch.Get());
+
+ // assert
+ if (endpointCollection.Count < 1 )
+ {
+ var errMsg = string.Format("No adapter-based LANEndpoint for external NIC {0} on Hyper-V server", externNIC.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ LANEndpoint adapterEndPoint = endpointCollection.OfType().First();
+ var switchEndpointQuery = new RelatedObjectQuery(adapterEndPoint.Path.Path, LANEndpoint.CreatedClassName);
+ var switchEndpointSearch = new ManagementObjectSearcher(externNIC.Scope, switchEndpointQuery);
+ var switchEndpointCollection = new LANEndpoint.LANEndpointCollection(switchEndpointSearch.Get());
+
+ // assert
+ if (endpointCollection.Count < 1)
+ {
+ var errMsg = string.Format("No Switch-based LANEndpoint for external NIC {0} on Hyper-V server", externNIC.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ LANEndpoint switchEndPoint = switchEndpointCollection.OfType().First();
+ var switchPortQuery = new RelatedObjectQuery(switchEndPoint.Path.Path, EthernetSwitchPort.CreatedClassName);
+ var switchPortSearch = new ManagementObjectSearcher(switchEndPoint.Scope, switchPortQuery);
+ var switchPortCollection = new EthernetSwitchPort.EthernetSwitchPortCollection(switchPortSearch.Get());
+
+ // assert
+ if (switchPortCollection.Count < 1 )
+ {
+ var errMsg = string.Format("No SwitchPort for external NIC {0} on Hyper-V server", externNIC.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ EthernetSwitchPort switchPort = switchPortCollection.OfType().First();
+ var vSwitchQuery = new RelatedObjectQuery(switchPort.Path.Path, VirtualEthernetSwitch.CreatedClassName);
+ var vSwitchSearch = new ManagementObjectSearcher(externNIC.Scope, vSwitchQuery);
+ var vSwitchCollection = new VirtualEthernetSwitch.VirtualEthernetSwitchCollection(vSwitchSearch.Get());
+
+ // assert
+ if (vSwitchCollection.Count < 1)
+ {
+ var errMsg = string.Format("No virtual switch for external NIC {0} on Hyper-V server", externNIC.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ VirtualEthernetSwitch vSwitch = vSwitchCollection.OfType().First();
+ return vSwitch;
+ }
+
+ private static void ModifyVmResources(VirtualSystemManagementService vmMgmtSvc, ComputerSystem vm, string[] resourceSettings)
+ {
+ // Resource settings are changed through the management service
+ System.Management.ManagementPath jobPath;
+ System.Management.ManagementPath[] results;
+
+ var ret_val = vmMgmtSvc.ModifyResourceSettings(
+ resourceSettings,
+ out jobPath,
+ out results);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
+ vm.ElementName,
+ vm.Name,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ public void DeleteHostKvpItem(ComputerSystem vm, string key)
+ {
+ // Obtain controller for Hyper-V virtualisation subsystem
+ VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
+
+ // Create object to hold the data.
+ KvpExchangeDataItem kvpItem = KvpExchangeDataItem.CreateInstance();
+ kvpItem.LateBoundObject["Name"] = WmiCallsV2.CloudStackUserDataKey;
+ kvpItem.LateBoundObject["Data"] = "dummy";
+ kvpItem.LateBoundObject["Source"] = 0;
+ logger.Debug("VM " + vm.Name + " will have KVP key " + key + " removed.");
+
+ String kvpStr = kvpItem.LateBoundObject.GetText(TextFormat.CimDtd20);
+
+ // Update the resource settings for the VM.
+ ManagementPath jobPath;
+
+ uint ret_val = vmMgmtSvc.RemoveKvpItems(new String[] { kvpStr }, vm.Path, out jobPath);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
+ vm.ElementName,
+ vm.Name,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ private static ComputerSystem CreateDefaultVm(VirtualSystemManagementService vmMgmtSvc, string name)
+ {
+ // Tweak default settings by basing new VM on default global setting object
+ // with designed display name.
+
+ VirtualSystemSettingData vs_gs_data = VirtualSystemSettingData.CreateInstance();
+ vs_gs_data.LateBoundObject["ElementName"] = name;
+
+ System.Management.ManagementPath jobPath;
+ System.Management.ManagementPath defined_sys;
+ var ret_val = vmMgmtSvc.DefineSystem(
+ null,
+ new string[0],
+ vs_gs_data.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20),
+ out jobPath,
+ out defined_sys);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to create VM {0} due to {1} (DefineVirtualSystem call)",
+ name, ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ logger.DebugFormat(CultureInfo.InvariantCulture, "Created VM {0}", name);
+
+ // Is the defined_system real?
+ var vm = new ComputerSystem(defined_sys);
+
+ // Assertion
+ if (vm.ElementName.CompareTo(name) != 0)
+ {
+ var errMsg = string.Format(
+ "New VM created with wrong name (is {0}, should be {1}, GUID {2})",
+ vm.ElementName,
+ name,
+ vm.Name);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return vm;
+ }
+
+ public VirtualEthernetSwitchManagementService GetVirtualSwitchManagementService()
+ {
+ // VirtualSwitchManagementService is a singleton, most anonymous way of lookup is by asking for the set
+ // of local instances, which should be size 1.
+ var virtSwtichSvcCollection = VirtualEthernetSwitchManagementService.GetInstances();
+ foreach (VirtualEthernetSwitchManagementService item in virtSwtichSvcCollection)
+ {
+ return item;
+ }
+
+ var errMsg = string.Format("No Hyper-V subsystem on server");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ///
+ /// Always produces a VHDX.
+ ///
+ ///
+ ///
+ public void CreateDynamicVirtualHardDisk(ulong MaxInternalSize, string Path)
+ {
+ // Produce description of the virtual disk in the format of a Msvm_VirtualHardDiskSettings object
+
+ // Example at http://www.getcodesamples.com/src/FC025DDC/76689747, but
+ // Is there a template we can use to fill in the settings?
+ var newVirtHDSettings = VirtualHardDiskSettingData.CreateInstance();
+ newVirtHDSettings.LateBoundObject["Type"] = 3; // Dynamic
+ newVirtHDSettings.LateBoundObject["Format"] = 3; // VHDX
+ newVirtHDSettings.LateBoundObject["Path"] = Path;
+ newVirtHDSettings.LateBoundObject["MaxInternalSize"] = MaxInternalSize;
+ newVirtHDSettings.LateBoundObject["BlockSize"] = 0; // Use defaults
+ newVirtHDSettings.LateBoundObject["LogicalSectorSize"] = 0; // Use defaults
+ newVirtHDSettings.LateBoundObject["PhysicalSectorSize"] = 0; // Use defaults
+
+ // Optional: newVirtHDSettings.CommitObject();
+
+ // Add the new vhd object as a virtual hard disk to the vm.
+ string newVirtHDSettingsString = newVirtHDSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20);
+
+ // Resource settings are changed through the management service
+ System.Management.ManagementPath jobPath;
+ var imgMgr = GetImageManagementService();
+ var ret_val = imgMgr.CreateVirtualHardDisk(newVirtHDSettingsString, out jobPath);
+
+ // If the Job is done asynchronously
+ if (ret_val == ReturnCode.Started)
+ {
+ JobCompleted(jobPath);
+ }
+ else if (ret_val != ReturnCode.Completed)
+ {
+ var errMsg = string.Format(
+ "Failed to CreateVirtualHardDisk size {0}, path {1} due to {2}",
+ MaxInternalSize,
+ Path,
+ ReturnCode.ToString(ret_val));
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+ }
+
+ public ImageManagementService GetImageManagementService()
+ {
+ // VirtualSystemManagementService is a singleton, most anonymous way of lookup is by asking for the set
+ // of local instances, which should be size 1.
+
+ var coll = ImageManagementService.GetInstances();
+ foreach (ImageManagementService item in coll)
+ {
+ return item;
+ }
+
+ var errMsg = string.Format("No Hyper-V subsystem on server");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+
+ public VirtualSystemManagementService GetVirtualisationSystemManagementService()
+ {
+ // VirtualSystemManagementService is a singleton, most anonymous way of lookup is by asking for the set
+ // of local instances, which should be size 1.
+
+ var virtSysMgmtSvcCollection = VirtualSystemManagementService.GetInstances();
+ foreach (VirtualSystemManagementService item in virtSysMgmtSvcCollection)
+ {
+ return item;
+ }
+
+ var errMsg = string.Format("No Hyper-V subsystem on server");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ///
+ /// Similar to http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
+ ///
+ ///
+ ///
+ private static void JobCompleted(ManagementPath jobPath)
+ {
+ ConcreteJob jobObj = null;
+ for(;;)
+ {
+ jobObj = new ConcreteJob(jobPath);
+ if (jobObj.JobState != JobState.Starting && jobObj.JobState != JobState.Running)
+ {
+ break;
+ }
+ logger.InfoFormat("In progress... {0}% completed.", jobObj.PercentComplete);
+ System.Threading.Thread.Sleep(1000);
+ }
+
+ if (jobObj.JobState != JobState.Completed)
+ {
+ var errMsg = string.Format(
+ "Hyper-V Job failed, Error Code:{0}, Description: {1}",
+ jobObj.ErrorCode,
+ jobObj.ErrorDescription);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ logger.DebugFormat("WMI job succeeded: {0}, Elapsed={1}", jobObj.Description, jobObj.ElapsedTime);
+ }
+
+ public void GetProcessorResources(out uint cores, out uint mhz)
+ {
+ // Processor processors
+ cores = 0;
+ mhz = 0;
+ Processor.ProcessorCollection procCol = Processor.GetInstances();
+ foreach (Processor procInfo in procCol)
+ {
+ cores += procInfo.NumberOfCores;
+ mhz = procInfo.MaxClockSpeed;
+ }
+ }
+
+ public void GetProcessorUsageInfo(out double cpuUtilization)
+ {
+ PerfFormattedData_Counters_ProcessorInformation.PerfFormattedData_Counters_ProcessorInformationCollection coll =
+ PerfFormattedData_Counters_ProcessorInformation.GetInstances("Name=\"_Total\"");
+ cpuUtilization = 100;
+ // Use the first one
+ foreach (PerfFormattedData_Counters_ProcessorInformation procInfo in coll)
+ {
+ // Idle during a given internal
+ // See http://library.wmifun.net/cimv2/win32_perfformatteddata_counters_processorinformation.html
+ cpuUtilization = 100.0 - (double)procInfo.PercentIdleTime;
+ }
+ }
+
+
+ public void GetMemoryResources(out ulong physicalRamKBs, out ulong freeMemoryKBs)
+ {
+ OperatingSystem0 os = new OperatingSystem0();
+ physicalRamKBs = os.TotalVisibleMemorySize;
+ freeMemoryKBs = os.FreePhysicalMemory;
+ }
+
+ public string GetDefaultVirtualDiskFolder()
+ {
+ VirtualSystemManagementServiceSettingData.VirtualSystemManagementServiceSettingDataCollection coll = VirtualSystemManagementServiceSettingData.GetInstances();
+ string defaultVirtualHardDiskPath = null;
+ foreach (VirtualSystemManagementServiceSettingData settings in coll)
+ {
+ defaultVirtualHardDiskPath = settings.DefaultVirtualHardDiskPath;
+ }
+
+ // assert
+ if (!System.IO.Directory.Exists(defaultVirtualHardDiskPath) ){
+ var errMsg = string.Format(
+ "Hyper-V DefaultVirtualHardDiskPath is invalid!");
+ logger.Error(errMsg);
+ return null;
+ }
+
+ return defaultVirtualHardDiskPath;
+ }
+
+ public ComputerSystem GetComputerSystem(string displayName)
+ {
+ var wmiQuery = String.Format("ElementName=\"{0}\"", displayName);
+ ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(wmiQuery);
+
+ // Return the first one
+ foreach (ComputerSystem vm in vmCollection)
+ {
+ return vm;
+ }
+ return null;
+ }
+
+ public List GetVmElementNames()
+ {
+ List result = new List();
+ ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances();
+
+ // Return the first one
+ foreach (ComputerSystem vm in vmCollection)
+ {
+ if (vm.Caption.StartsWith("Hosting Computer System") )
+ {
+ continue;
+ }
+ result.Add(vm.ElementName);
+ }
+ return result;
+ }
+
+ public ProcessorSettingData GetProcSettings(VirtualSystemSettingData vmSettings)
+ {
+ // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
+ // ProcessorSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, ProcessorSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
+ var wmiObjCollection = new ProcessorSettingData.ProcessorSettingDataCollection(wmiObjectSearch.Get());
+
+ foreach (ProcessorSettingData wmiObj in wmiObjCollection)
+ {
+ return wmiObj;
+ }
+
+ var errMsg = string.Format("No ProcessorSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ public MemorySettingData GetMemSettings(VirtualSystemSettingData vmSettings)
+ {
+ // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
+ // MemorySettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, MemorySettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
+ var wmiObjCollection = new MemorySettingData.MemorySettingDataCollection(wmiObjectSearch.Get());
+
+ foreach (MemorySettingData wmiObj in wmiObjCollection)
+ {
+ return wmiObj;
+ }
+
+ var errMsg = string.Format("No MemorySettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+
+ public ResourceAllocationSettingData GetDvdDriveSettings(VirtualSystemSettingData vmSettings)
+ {
+ var wmiObjCollection = GetResourceAllocationSettings(vmSettings);
+
+ foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection)
+ {
+ // DVD drive is '16', see http://msdn.microsoft.com/en-us/library/hh850200(v=vs.85).aspx
+ if (wmiObj.ResourceType == 16)
+ {
+ return wmiObj;
+ }
+ }
+
+ var errMsg = string.Format(
+ "Cannot find the Dvd drive in VirtualSystemSettingData {0}",
+ vmSettings.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ public ResourceAllocationSettingData GetIDEControllerSettings(VirtualSystemSettingData vmSettings, string cntrllerAddr)
+ {
+ var wmiObjCollection = GetResourceAllocationSettings(vmSettings);
+
+ foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection)
+ {
+ if (wmiObj.ResourceSubType == IDE_HARDDISK_CONTROLLER && wmiObj.Address == cntrllerAddr)
+ {
+ return wmiObj;
+ }
+ }
+
+ var errMsg = string.Format(
+ "Cannot find the Microsoft Emulated IDE Controlle at address {0} in VirtualSystemSettingData {1}",
+ cntrllerAddr,
+ vmSettings.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ ///
+ /// VM resources, typically hardware a described by a generic MSVM_ResourceAllocationSettingData object. The hardware type being
+ /// described is identified in two ways: in general terms using an enum in the ResourceType field, and in terms of the implementation
+ /// using text in the ResourceSubType field.
+ /// See http://msdn.microsoft.com/en-us/library/cc136877%28v=vs.85%29.aspx
+ ///
+ ///
+ ///
+ public ResourceAllocationSettingData.ResourceAllocationSettingDataCollection GetResourceAllocationSettings(VirtualSystemSettingData vmSettings)
+ {
+ // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
+ // ResourceAllocationSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, ResourceAllocationSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
+ var wmiObjCollection = new ResourceAllocationSettingData.ResourceAllocationSettingDataCollection(wmiObjectSearch.Get());
+
+ if (wmiObjCollection != null)
+ {
+ return wmiObjCollection;
+ }
+
+ var errMsg = string.Format("No ResourceAllocationSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ public EthernetPortAllocationSettingData[] GetEthernetConnections(ComputerSystem vm)
+ {
+ // ComputerSystem -> VirtualSystemSettingData -> EthernetPortAllocationSettingData
+ VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+
+ // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
+ // EthernetPortAllocationSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, EthernetPortAllocationSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
+ var wmiObjCollection = new EthernetPortAllocationSettingData.EthernetPortAllocationSettingDataCollection(wmiObjectSearch.Get());
+
+ var result = new List(wmiObjCollection.Count);
+ foreach (EthernetPortAllocationSettingData item in wmiObjCollection)
+ {
+ result.Add(item);
+ }
+ return result.ToArray();
+ }
+
+
+ public EthernetSwitchPortVlanSettingData GetVlanSettings(EthernetPortAllocationSettingData ethernetConnection)
+ {
+ // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
+ // EthernetPortAllocationSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(ethernetConnection.Path.Path, EthernetSwitchPortVlanSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(ethernetConnection.Scope, wmiObjQuery);
+ var wmiObjCollection = new EthernetSwitchPortVlanSettingData.EthernetSwitchPortVlanSettingDataCollection(wmiObjectSearch.Get());
+
+ if (wmiObjCollection.Count == 0)
+ {
+ return null;
+ }
+
+ // Assert
+ if (wmiObjCollection.Count > 1)
+ {
+ var errMsg = string.Format("Internal error, morn one VLAN settings for a single ethernetConnection");
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ return wmiObjCollection.OfType().First();
+ }
+
+
+ public SyntheticEthernetPortSettingData[] GetEthernetPortSettings(ComputerSystem vm)
+ {
+ // An ASSOCIATOR object provides the cross reference from the ComputerSettings and the
+ // SyntheticEthernetPortSettingData, via the VirtualSystemSettingData.
+ // However, generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ //
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName);
+ //
+ VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+
+ var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, SyntheticEthernetPortSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vm.Scope, wmiObjQuery);
+ var wmiObjCollection = new SyntheticEthernetPortSettingData.SyntheticEthernetPortSettingDataCollection(wmiObjectSearch.Get());
+
+ List results = new List(wmiObjCollection.Count);
+ foreach (SyntheticEthernetPortSettingData item in wmiObjCollection)
+ {
+ results.Add(item);
+ }
+
+ return results.ToArray();
+ }
+
+ public string GetDefaultDataRoot()
+ {
+ string defaultRootPath = null;
+ VirtualSystemManagementServiceSettingData vs_mgmt_data = VirtualSystemManagementServiceSettingData.CreateInstance();
+ defaultRootPath = vs_mgmt_data.DefaultVirtualHardDiskPath;
+ if (defaultRootPath == null) {
+ defaultRootPath = Path.GetPathRoot(Environment.SystemDirectory) +
+ "\\Users\\Public\\Documents\\Hyper-V\\Virtual hard disks";
+ }
+
+ return defaultRootPath;
+ }
+
+ public VirtualSystemSettingData GetVmSettings(ComputerSystem vm)
+ {
+ // An ASSOCIATOR object provides the cross reference from the ComputerSettings and the
+ // VirtualSystemSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(vm.Path.Path, VirtualSystemSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vm.Scope, wmiObjQuery);
+ var wmiObjCollection = new VirtualSystemSettingData.VirtualSystemSettingDataCollection(wmiObjectSearch.Get());
+
+ // When snapshots are taken into account, there can be multiple settings objects
+ // take the first one that isn't a snapshot
+ foreach (VirtualSystemSettingData wmiObj in wmiObjCollection)
+ {
+ if (wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Realized" ||
+ wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Planned")
+ {
+ return wmiObj;
+ }
+ }
+
+ var errMsg = string.Format("No VirtualSystemSettingData for VM {0}, path {1}", vm.ElementName, vm.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ public KvpExchangeComponentSettingData GetKvpSettings(VirtualSystemSettingData vmSettings)
+ {
+ // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
+ // KvpExchangeComponentSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
+ // Instead, we use the System.Management to code the equivalant of
+ // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
+ //
+ var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, KvpExchangeComponentSettingData.CreatedClassName);
+
+ // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
+ // the virtualisation objects.
+ var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
+ var wmiObjCollection = new KvpExchangeComponentSettingData.KvpExchangeComponentSettingDataCollection(wmiObjectSearch.Get());
+
+ foreach (KvpExchangeComponentSettingData wmiObj in wmiObjCollection)
+ {
+ return wmiObj;
+ }
+
+ var errMsg = string.Format("No KvpExchangeComponentSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
+ var ex = new WmiException(errMsg);
+ logger.Error(errMsg, ex);
+ throw ex;
+ }
+
+ public void GetSummaryInfo(Dictionary vmProcessorInfo, List vmsToInspect)
+ {
+ // Process info available from WMI,
+ // See http://msdn.microsoft.com/en-us/library/hh850062(v=vs.85).aspx
+ uint[] requestedInfo = new uint[] { // TODO: correct?
+ 0, // Name
+ 1, // ElementName
+ 4, // Number of processes
+ 101 // ProcessorLoad
+ };
+
+ System.Management.ManagementBaseObject[] sysSummary;
+ var vmsvc = GetVirtualisationSystemManagementService();
+ System.Management.ManagementPath[] vmPaths = vmsToInspect.ToArray();
+ vmsvc.GetSummaryInformation(requestedInfo, vmPaths, out sysSummary);
+
+ foreach (var summary in sysSummary)
+ {
+
+ var summaryInfo = new SummaryInformation(summary);
+
+ logger.Debug("VM " + summaryInfo.Name + "(elementName " + summaryInfo.ElementName + ") has " +
+ summaryInfo.NumberOfProcessors + " CPUs, and load of " + summaryInfo.ProcessorLoad);
+ var vmInfo = new VmStatsEntry
+ {
+ cpuUtilization = summaryInfo.ProcessorLoad,
+ numCPUs = summaryInfo.NumberOfProcessors,
+ networkReadKBs = 1,
+ networkWriteKBs = 1,
+ entityType = "vm"
+ };
+ vmProcessorInfo.Add(summaryInfo.ElementName, vmInfo);
+ }
+ }
+ }
+
+ public class WmiException : Exception
+ {
+ public WmiException()
+ {
+ }
+
+ public WmiException(string message)
+ : base(message)
+ {
+ }
+
+ public WmiException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+ }
+
+ ///
+ /// Covers V2 API, see
+ /// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
+ ///
+ public static class ReturnCode
+ {
+ public const UInt32 Completed = 0;
+ public const UInt32 Started = 4096;
+ public const UInt32 Failed = 32768;
+ public const UInt32 AccessDenied = 32769;
+ public const UInt32 NotSupported = 32770;
+ public const UInt32 Unknown = 32771;
+ public const UInt32 Timeout = 32772;
+ public const UInt32 InvalidParameter = 32773;
+ public const UInt32 SystemInUse = 32774;
+ public const UInt32 InvalidState = 32775;
+ public const UInt32 IncorrectDataType = 32776;
+ public const UInt32 SystemNotAvailable = 32777;
+ public const UInt32 OutofMemory = 32778;
+ public static string ToString(UInt32 value)
+ {
+ string result = "Unknown return code";
+ switch (value)
+ {
+ case Completed: result = "Completed"; break;
+ case Started: result = "Started"; break;
+ case Failed: result = "Failed"; break;
+ case AccessDenied: result = "AccessDenied"; break;
+ case NotSupported: result = "NotSupported"; break;
+ case Unknown: result = "Unknown"; break;
+ case Timeout: result = "Timeout"; break;
+ case InvalidParameter: result = "InvalidParameter"; break;
+ case SystemInUse: result = "SystemInUse"; break;
+ case InvalidState: result = "InvalidState"; break;
+ case IncorrectDataType: result = "IncorrectDataType"; break;
+ case SystemNotAvailable: result = "SystemNotAvailable"; break;
+ case OutofMemory: result = "OutofMemory"; break;
+ }
+ return result;
+ }
+ }
+
+ ///
+ /// Covers V2 API, see
+ /// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
+ ///
+ public static class JobState
+ {
+ public const UInt16 New = 2;
+ public const UInt16 Starting = 3;
+ public const UInt16 Running = 4;
+ public const UInt16 Suspended = 5;
+ public const UInt16 ShuttingDown = 6;
+ public const UInt16 Completed = 7;
+ public const UInt16 Terminated = 8;
+ public const UInt16 Killed = 9;
+ public const UInt16 Exception = 10;
+ public const UInt16 Service = 11;
+ public static string ToString(UInt16 value)
+ {
+ string result = "Unknown JobState code";
+ switch (value)
+ {
+ case New: result = "New"; break;
+ case Starting: result = "Starting"; break;
+ case Running: result = "Running"; break;
+ case Suspended: result = "Suspended"; break;
+ case ShuttingDown: result = "ShuttingDown"; break;
+ case Completed: result = "Completed"; break;
+ case Terminated: result = "Terminated"; break;
+ case Killed: result = "Killed"; break;
+ case Exception: result = "Exception"; break;
+ case Service: result = "Service"; break;
+ }
+ return result;
+ }
+ }
+
+ ///
+ /// V2 API (see http://msdn.microsoft.com/en-us/library/hh850279(v=vs.85).aspx)
+ /// has removed 'Paused' and 'Suspended' as compared to the
+ /// V1 API (see http://msdn.microsoft.com/en-us/library/cc723874%28v=vs.85%29.aspx)
+ /// However, Paused and Suspended appear on the VM state transition table
+ /// (see http://msdn.microsoft.com/en-us/library/hh850116(v=vs.85).aspx#methods)
+ ///
+ public class RequiredState
+ {
+ public const UInt16 Enabled = 2; // Turns the VM on.
+ public const UInt16 Disabled = 3; // Turns the VM off.
+ public const UInt16 ShutDown = 4;
+ public const UInt16 Offline = 6;
+ //public const UInt16 Test = 7;
+ public const UInt16 Defer = 8;
+ // public const UInt16 Quiesce = 9;
+ // public const UInt16 Reboot = 10; // A hard reset of the VM.
+ public const UInt16 Reset = 11; // For future use.
+ public const UInt16 Paused = 9; // Pauses the VM.
+ public const UInt16 Suspended = 32779; // Saves the state of the VM.
+
+ public static string ToString(UInt16 value)
+ {
+ string result = "Unknown RequiredState code";
+ switch (value)
+ {
+ case Enabled: result = "Enabled"; break;
+ case Disabled: result = "Disabled"; break;
+ case ShutDown: result = "ShutDown"; break;
+ case Offline: result = "Offline"; break;
+ case Defer: result = "Defer"; break;
+ case Reset: result = "Reset"; break;
+ }
+ return result;
+ }
+ }
+
+ ///
+ /// V2 API specifies the states below in its state transition graph at
+ /// http://msdn.microsoft.com/en-us/library/hh850116(v=vs.85).aspx#methods
+ /// However, the CIM standard has additional possibilities based on the description
+ /// of EnabledState.
+ /// The previous V1 API is described by
+ /// http://msdn.microsoft.com/en-us/library/cc136822%28v=vs.85%29.aspx
+ ///
+ public class EnabledState
+ {
+ ///
+ /// The state of the VM could not be determined.
+ ///
+ public const UInt16 Unknown = 0;
+ ///
+ /// The VM is running.
+ ///
+ public const UInt16 Enabled = 2;
+ ///
+ /// The VM is turned off.
+ ///
+ public const UInt16 Disabled = 3;
+ ///
+ /// The VM is paused.
+ ///
+ public const UInt16 Paused = 32768;
+ ///
+ /// The VM is in a saved state.
+ ///
+ public const UInt16 Suspended = 32769;
+ ///
+ /// The VM is starting. This is a transitional state between 3 (Disabled)
+ /// or 32769 (Suspended) and 2 (Enabled) initiated by a call to the
+ /// RequestStateChange method with a RequestedState parameter of 2 (Enabled).
+ ///
+ public const UInt16 Starting = 32770;
+ ///
+ /// Starting with Windows Server 2008 R2 this value is not supported.
+ /// If the VM is performing a snapshot operation, the element at index 1
+ /// of the OperationalStatus property array will contain 32768 (Creating Snapshot),
+ /// 32769 (Applying Snapshot), or 32770 (Deleting Snapshot).
+ ///
+ public const UInt16 Snapshotting = 32771;
+ ///
+ /// The VM is saving its state. This is a transitional state between 2 (Enabled)
+ /// and 32769 (Suspended) initiated by a call to the RequestStateChange method
+ /// with a RequestedState parameter of 32769 (Suspended).
+ ///
+ public const UInt16 Saving = 32773;
+ ///
+ /// The VM is turning off. This is a transitional state between 2 (Enabled)
+ /// and 3 (Disabled) initiated by a call to the RequestStateChange method
+ /// with a RequestedState parameter of 3 (Disabled) or a guest operating system
+ /// initiated power off.
+ ///
+ public const UInt16 Stopping = 32774;
+ ///
+ /// The VM is pausing. This is a transitional state between 2 (Enabled) and 32768 (Paused) initiated by a call to the RequestStateChange method with a RequestedState parameter of 32768 (Paused).
+ ///
+ public const UInt16 Pausing = 32776;
+ ///
+ /// The VM is resuming from a paused state. This is a transitional state between 32768 (Paused) and 2 (Enabled).
+ ///
+ public const UInt16 Resuming = 32777;
+
+ public static string ToString(UInt16 value)
+ {
+ string result = "Unknown";
+ switch (value)
+ {
+ case Enabled: result = "Enabled"; break;
+ case Disabled: result = "Disabled"; break;
+ case Paused: result = "Paused"; break;
+ case Suspended: result = "Suspended"; break;
+ case Starting: result = "Starting"; break;
+ case Snapshotting: result = "Snapshotting"; break; // NOT used
+ case Saving: result = "Saving"; break;
+ case Stopping: result = "Stopping"; break;
+ case Pausing: result = "Pausing"; break;
+ case Resuming: result = "Resuming"; break;
+ }
+ return result;
+ }
+
+ public static string ToCloudStackState(UInt16 value)
+ {
+ string result = "Unknown";
+ switch (value)
+ {
+ case Enabled: result = "Running"; break;
+ case Disabled: result = "Stopped"; break;
+ case Paused: result = "Unknown"; break;
+ case Suspended: result = "Unknown"; break;
+ case Starting: result = "Starting"; break;
+ case Snapshotting: result = "Unknown"; break; // NOT used
+ case Saving: result = "Saving"; break;
+ case Stopping: result = "Stopping"; break;
+ case Pausing: result = "Unknown"; break;
+ case Resuming: result = "Starting"; break;
+ }
+ return result;
+ }
+ }
+}
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/packages.config b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/packages.config
new file mode 100644
index 00000000000..4c538e4872b
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/App.config b/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/App.config
new file mode 100644
index 00000000000..c959ccf1443
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/App.config
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 10.1.1.1
+
+
+ Routing
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ hvm
+
+
+ 2130
+
+
+ 8250
+
+
+ 34359738368
+
+
+ camldonall01.citrite.net
+
+
+ 1
+
+
+ 10.70.176.1
+
+
+ 2
+
+
+ 1
+
+
+ E:\Disks\Disks
+
+
+ 5
+
+
+ 255.255.240.0
+
+
+ 4294967296
+
+
+ ..\..\..\..\..\
+
+
+ e:\
+
+
+ 2048
+
+
+ 101F742C6B88
+
+
+
+
+
+
+ c:\Secondary
+
+
+ E:\Disks\Disks
+
+
+ 5fe2bad3-d785-394e-9949-89786b8a63d2
+
+
+ .\var\test\storagepool
+
+
+ cshv3eu
+
+
+ s3.amazonaws.com
+
+
+ testS3AccessKey
+
+
+ testS3SecretKey
+
+
+ 206-2-73592258-559a-3b38-8f66-b667aab143eb
+
+
+ 206-2-73592258-559a-3b38-8f66-b667aab143eb
+
+
+ cifs://10.1.1.1/secondary?user\u003dadministrator\u0026password\u003d1pass%40word1
+
+
+ template/tmpl/2/201/6dda6631-4daa-3150-a49a-d5a4b0a4c4b6.vhd
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceController1Test.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceController1Test.cs
new file mode 100644
index 00000000000..fd0a4bf2f7e
--- /dev/null
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceController1Test.cs
@@ -0,0 +1,350 @@
+// 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.
+using System;
+using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2;
+using System.Management;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+using System.IO;
+using log4net;
+using HypervResource;
+using CloudStack.Plugin.AgentShell;
+using System.Collections.Generic;
+using NSubstitute;
+using System.Web.Http;
+using Xunit;
+
+namespace ServerResource.Tests
+{
+ public class HypervResourceController1Test
+ {
+ protected static string testCifsUrl = AgentSettings.Default.testCifsUrl;
+ protected static string testCifsPath = AgentSettings.Default.testCifsPath;
+ protected static String testPrimaryDataStoreHost = HypervResourceController.config.StorageIpAddress;
+ protected static String testS3TemplateName = AgentSettings.Default.testS3TemplateName;
+ protected static String testCifsTemplateName = AgentSettings.Default.testS3TemplateName;
+ protected static String testSystemVMTemplateName = AgentSettings.Default.testSystemVMTemplateName;
+ protected static String testSystemVMTemplateNameNoExt = AgentSettings.Default.testSystemVMTemplateNameNoExt;
+ protected static String testLocalStoreUUID = "5fe2bad3-d785-394e-9949-89786b8a63d2";
+ protected static String testLocalStorePath = Path.Combine(AgentSettings.Default.hyperv_plugin_root, "var", "test", "storagepool");
+ protected static String testSecondaryStoreLocalPath = Path.Combine(AgentSettings.Default.hyperv_plugin_root, "var", "test", "secondary");
+
+ // TODO: differentiate between NFS and HTTP template URLs.
+ protected static String testSampleTemplateUUID = "TestCopiedLocalTemplate.vhdx";
+ protected static String testSampleTemplateURL = testSampleTemplateUUID;
+
+ // test volumes are both a minimal size vhdx. Changing the extension to .vhd makes on corrupt.
+ protected static String testSampleVolumeWorkingUUID = "TestVolumeLegit.vhdx";
+ protected static String testSampleVolumeCorruptUUID = "TestVolumeCorrupt.vhd";
+ protected static String testSampleVolumeTempUUID = "TestVolumeTemp.vhdx";
+ protected static String testSampleVolumeTempUUIDNoExt = "TestVolumeTemp";
+ protected static String testSampleVolumeWorkingURIJSON;
+ protected static String testSampleVolumeCorruptURIJSON;
+ protected static String testSampleVolumeTempURIJSON;
+
+ protected static String testSampleTemplateURLJSON;
+ protected static String testLocalStorePathJSON;
+
+ protected static IWmiCallsV2 wmiCallsV2;
+
+
+ private static ILog s_logger = LogManager.GetLogger(typeof(HypervResourceController1Test));
+
+ ///
+ /// Test WmiCalls to which incoming HTTP POST requests are dispatched.
+ ///
+ /// TODO: revise beyond first approximation
+ /// First approximation is a quick port of the existing Java tests for Hyper-V server resource.
+ /// A second approximation would use the AgentShell settings files directly.
+ /// A third approximation would look to invoke ServerResource methods via an HTTP request
+ ///
+
+ public HypervResourceController1Test()
+ {
+ wmiCallsV2 = Substitute.For();
+ //AgentService.ConfigServerResource();
+ HypervResourceController.config.PrivateMacAddress = AgentSettings.Default.private_mac_address;
+ HypervResourceController.config.PrivateNetmask = AgentSettings.Default.private_ip_netmask;
+ HypervResourceController.config.StorageIpAddress = HypervResourceController.config.PrivateIpAddress;
+ HypervResourceController.config.StorageMacAddress = HypervResourceController.config.PrivateMacAddress;
+ HypervResourceController.config.StorageNetmask = HypervResourceController.config.PrivateNetmask;
+
+
+ // Used to create existing StoragePool in preparation for the ModifyStoragePool
+ testLocalStoreUUID = AgentSettings.Default.local_storage_uuid.ToString();
+
+ // Make sure secondary store is available.
+ string fullPath = Path.GetFullPath(testSecondaryStoreLocalPath);
+ s_logger.Info("Test secondary storage in " + fullPath);
+ DirectoryInfo testSecondarStoreDir = new DirectoryInfo(fullPath);
+ if (!testSecondarStoreDir.Exists)
+ {
+ try
+ {
+ testSecondarStoreDir.Create();
+ }
+ catch (System.IO.IOException ex)
+ {
+ throw new NotImplementedException("Need to be able to create the folder " + testSecondarStoreDir.FullName + " failed due to " + ex.Message);
+ }
+ }
+
+ // Convert to secondary storage string to canonical path
+ testSecondaryStoreLocalPath = testSecondarStoreDir.FullName;
+ AgentSettings.Default.local_secondary_storage_path = testSecondaryStoreLocalPath;
+
+ // Make sure local primary storage is available
+ DirectoryInfo testPoolDir = new DirectoryInfo(testLocalStorePath);
+ //Assert.True(testPoolDir.Exists, "To simulate local file system Storage Pool, you need folder at " + testPoolDir.FullName);
+
+ // Convert to local primary storage string to canonical path
+ testLocalStorePath = testPoolDir.FullName;
+ AgentSettings.Default.local_storage_path = testLocalStorePath;
+
+ // Clean up old test files in local storage folder
+ FileInfo testVolWorks = new FileInfo(Path.Combine(testLocalStorePath, testSampleVolumeWorkingUUID));
+ // Assert.True(testVolWorks.Exists, "Create a working virtual disk at " + testVolWorks.FullName);
+
+ testSampleTemplateURLJSON = JsonConvert.SerializeObject(testSampleTemplateUUID);
+ s_logger.Info("Created " + testSampleTemplateURLJSON + " in local storage.");
+
+
+ // Capture other JSON encoded paths
+ testSampleVolumeWorkingURIJSON = Newtonsoft.Json.JsonConvert.SerializeObject(testVolWorks.FullName);
+ testLocalStorePathJSON = JsonConvert.SerializeObject(testLocalStorePath);
+
+ // TODO: may need to initialise the server resource in future.
+ // s_hypervresource.initialize();
+
+ // Verify sample template is in place storage pool
+ s_logger.Info("setUp complete, sample StoragePool at " + testLocalStorePathJSON
+ + " sample template at " + testSampleTemplateURLJSON);
+ }
+
+ private String CreateTestDiskImageFromExistingImage(FileInfo srcFile,
+ String dstPath,
+ String dstFileName)
+ {
+ var newFullname = Path.Combine(dstPath, dstFileName);
+ var newFileInfo = new FileInfo(newFullname);
+ if (!newFileInfo.Exists)
+ {
+ newFileInfo = srcFile.CopyTo(newFullname);
+ }
+ newFileInfo.Refresh();
+ Assert.True(newFileInfo.Exists, "Attempted to create " + newFullname + " from " + newFileInfo.FullName);
+
+ return JsonConvert.SerializeObject(newFileInfo.FullName);
+ }
+
+ [Fact]
+ public void TestCreateCommand()
+ {
+ DirectoryInfo localStorePath = new DirectoryInfo(testLocalStorePath);
+ if (!localStorePath.Exists)
+ {
+ try
+ {
+ localStorePath.Create();
+ }
+ catch (System.IO.IOException ex)
+ {
+ throw new NotImplementedException("Need to be able to create the folder " + localStorePath.FullName + " failed due to " + ex.Message);
+ }
+ }
+
+ FileInfo sampleTemplateFile = new FileInfo(Path.Combine(testLocalStorePath, testSampleTemplateUUID));
+ if (!sampleTemplateFile.Exists)
+ {
+ //Create a file to write to.
+ using (StreamWriter sw = sampleTemplateFile.CreateText())
+ {
+ sw.WriteLine("This is fake template file for test");
+ }
+ }
+ var counter = 0;
+ wmiCallsV2.When(x => x.CreateDynamicVirtualHardDisk(Arg.Any(), Arg.Any())).Do(x => counter++);
+ // TODO: Need sample to update the test.
+ // Arrange
+ String createCmd = "{\"volId\":10,\"pool\":{\"id\":201,\"uuid\":\"" + testLocalStoreUUID + "\",\"host\":\"" + HypervResourceController.config.StorageIpAddress + "\"" +
+ ",\"path\":" + testLocalStorePathJSON + ",\"port\":0,\"type\":\"Filesystem\"},\"diskCharacteristics\":{\"size\":0," +
+ "\"tags\":[],\"type\":\"ROOT\",\"name\":\"ROOT-9\",\"useLocalStorage\":true,\"recreatable\":true,\"diskOfferingId\":11," +
+ "\"volumeId\":10,\"hyperType\":\"Hyperv\"},\"templateUrl\":" + testSampleTemplateURLJSON + ",\"contextMap\":{},\"wait\":0}";
+ dynamic jsonCreateCmd = JsonConvert.DeserializeObject(createCmd);
+ HypervResourceController rsrcServer = new HypervResourceController();
+ HypervResourceController.wmiCallsV2 = wmiCallsV2;
+
+ Assert.True(Directory.Exists(testLocalStorePath), testLocalStorePath + " does not exist ");
+ string filePath = Path.Combine(testLocalStorePath, (string)JsonConvert.DeserializeObject(testSampleTemplateURLJSON));
+ Assert.True(File.Exists(filePath), "The template we make volumes from is missing from path " + filePath);
+ int fileCount = Directory.GetFiles(testLocalStorePath).Length;
+ s_logger.Debug(" test local store has " + fileCount + "files");
+
+ // Act
+ // Test requires there to be a template at the tempalteUrl, which is its location in the local file system.
+ dynamic jsonResult = rsrcServer.CreateCommand(jsonCreateCmd);
+ s_logger.Debug("CreateDynamicVirtualHardDisk method is called " + counter + " times");
+
+ //Assert.Equal(counter, 1);
+
+ JObject ansAsProperty2 = jsonResult[0];
+ dynamic ans = ansAsProperty2.GetValue(CloudStackTypes.CreateAnswer);
+ Assert.NotNull(ans);
+ Assert.True((bool)ans.result, "Failed to CreateCommand due to " + (string)ans.result);
+ Assert.Equal(Directory.GetFiles(testLocalStorePath).Length, fileCount + 1);
+ FileInfo newFile = new FileInfo((string)ans.volume.path);
+ Assert.True(newFile.Length > 0, "The new file should have a size greater than zero");
+ newFile.Delete();
+ sampleTemplateFile.Delete();
+ }
+
+ [Fact]
+ public void TestDestroyCommand()
+ {
+ testSampleVolumeTempURIJSON = "\"storagepool\"";
+ // Arrange
+ String destoryCmd = //"{\"volume\":" + getSampleVolumeObjectTO() + "}";
+ "{\"volume\":{\"name\":\"" + testSampleVolumeTempUUIDNoExt
+ + "\",\"storagePoolType\":\"Filesystem\","
+ + "\"mountPoint\":"
+ + testLocalStorePathJSON
+ + ",\"path\":" + testSampleVolumeTempURIJSON
+ + ",\"storagePoolUuid\":\"" + testLocalStoreUUID
+ + "\","
+ + "\"type\":\"ROOT\",\"id\":9,\"size\":0}}";
+
+ ImageManagementService imgmgr = new ImageManagementService();
+ wmiCallsV2.GetImageManagementService().Returns(imgmgr);
+
+ HypervResourceController rsrcServer = new HypervResourceController();
+ HypervResourceController.wmiCallsV2 = wmiCallsV2;
+
+ dynamic jsonDestoryCmd = JsonConvert.DeserializeObject(destoryCmd);
+
+ // Act
+ dynamic destoryAns = rsrcServer.DestroyCommand(jsonDestoryCmd);
+
+ // Assert
+ JObject ansAsProperty2 = destoryAns[0];
+ dynamic ans = ansAsProperty2.GetValue(CloudStackTypes.Answer);
+ String path = jsonDestoryCmd.volume.path;
+ Assert.True((bool)ans.result, "DestroyCommand did not succeed " + ans.details);
+ Assert.True(!File.Exists(path), "Failed to delete file " + path);
+ }
+
+ [Fact]
+ public void TestStartCommand()
+ {
+ ComputerSystem system = new ComputerSystem();
+ wmiCallsV2.DeployVirtualMachine(Arg.Any