diagnostics: new diagnostics admin API for system VMs (#2721)

This is a new feature for CS that allows Admin users improved
troubleshooting of network issues in CloudStack hosted networks.

Description: For troubleshooting purposes, CloudStack administrators may wish to execute network utility commands remotely on system VMs, or request system VMs to ping/traceroute/arping to specific addresses over specific interfaces. An API command to provide such functionalities is being developed without altering any existing APIs. The targeted system VMs for this feature are the Virtual Router (VR), Secondary Storage VM (SSVM) and the Console Proxy VM (CPVM).

FS:
https://cwiki.apache.org/confluence/display/CLOUDSTACK/CloudStack+Remote+Diagnostics+API
ML discussion:
https://markmail.org/message/xt7owmb2c6iw7tva
This commit is contained in:
Dingane Hlaluku 2018-07-13 13:28:45 +02:00 committed by Rohit Yadav
parent 07042a67c6
commit 40af32b1b9
19 changed files with 1377 additions and 28 deletions

View File

@ -38,7 +38,7 @@ env:
matrix:
# Keep the TESTS sorted by name and grouped by type
- TESTS="smoke/test_certauthority_root"
- TESTS="smoke/test_accounts
smoke/test_affinity_groups
smoke/test_affinity_groups_projects
@ -47,6 +47,7 @@ env:
smoke/test_deploy_vm_root_resize
smoke/test_deploy_vm_with_userdata
smoke/test_deploy_vms_with_varied_deploymentplanners
smoke/test_diagnostics
smoke/test_disk_offerings
smoke/test_dynamicroles
smoke/test_global_settings

View File

@ -719,6 +719,11 @@ public class ApiConstants {
public static final String LAST_ANNOTATED = "lastannotated";
public static final String LDAP_DOMAIN = "ldapdomain";
public static final String STDOUT = "stdout";
public static final String STDERR = "stderr";
public static final String EXITCODE = "exitcode";
public static final String TARGET_ID = "targetid";
public enum HostDetails {
all, capacity, events, stats, min;
}

View File

@ -0,0 +1,136 @@
// 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.api.command.admin.diagnostics;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.RunDiagnosticsResponse;
import org.apache.cloudstack.api.response.SystemVmResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.diagnostics.DiagnosticsService;
import org.apache.cloudstack.diagnostics.DiagnosticsType;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import javax.inject.Inject;
import java.util.Collections;
import java.util.Map;
@APICommand(name = RunDiagnosticsCmd.APINAME, responseObject = RunDiagnosticsResponse.class, entityType = {VirtualMachine.class},
responseHasSensitiveInfo = false,
requestHasSensitiveInfo = false,
description = "Execute network-utility command (ping/arping/tracert) on system VMs remotely",
authorized = {RoleType.Admin},
since = "4.12.0.0")
public class RunDiagnosticsCmd extends BaseCmd {
private static final Logger LOGGER = Logger.getLogger(RunDiagnosticsCmd.class);
public static final String APINAME = "runDiagnostics";
@Inject
private DiagnosticsService diagnosticsService;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.TARGET_ID, type = CommandType.UUID, required = true, entityType = SystemVmResponse.class,
validations = {ApiArgValidator.PositiveNumber},
description = "The ID of the system VM instance to diagnose")
private Long id;
@Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = true,
validations = {ApiArgValidator.NotNullOrEmpty},
description = "The IP/Domain address to test connection to")
private String address;
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true,
validations = {ApiArgValidator.NotNullOrEmpty},
description = "The system VM diagnostics type valid options are: ping, traceroute, arping")
private String type;
@Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING,
authorized = {RoleType.Admin},
description = "Additional command line options that apply for each command")
private String optionalArguments;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getId() {
return id;
}
public String getAddress() {
return address;
}
public DiagnosticsType getType() {
DiagnosticsType diagnosticsType = DiagnosticsType.getCommand(type);
if (diagnosticsType == null) {
throw new IllegalArgumentException(type + " Is not a valid diagnostics command type. ");
}
return diagnosticsType;
}
public String getOptionalArguments() {
return optionalArguments;
}
/////////////////////////////////////////////////////
/////////////////// Implementation //////////////////
/////////////////////////////////////////////////////
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}
@Override
public long getEntityOwnerId() {
Account account = CallContext.current().getCallingAccount();
if (account != null) {
return account.getId();
}
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException {
RunDiagnosticsResponse response = new RunDiagnosticsResponse();
try {
final Map<String, String> answerMap = diagnosticsService.runDiagnosticsCommand(this);
if (CollectionUtils.isNotEmpty(Collections.singleton(answerMap))) {
response.setStdout(answerMap.get(ApiConstants.STDOUT));
response.setStderr(answerMap.get(ApiConstants.STDERR));
response.setExitCode(answerMap.get(ApiConstants.EXITCODE));
response.setObjectName("diagnostics");
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
} catch (final ServerApiException e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
}

View File

@ -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.
//
package org.apache.cloudstack.api.response;
import com.cloud.serializer.Param;
import com.cloud.vm.VirtualMachine;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
@EntityReference(value = VirtualMachine.class)
public class RunDiagnosticsResponse extends BaseResponse {
@SerializedName(ApiConstants.STDOUT)
@Param(description = "the standard output from the command execution")
private String stdout;
@SerializedName(ApiConstants.STDERR)
@Param(description = "the standard error output from the command execution")
private String stderr;
@SerializedName(ApiConstants.EXITCODE)
@Param(description = "the command execution return code")
private String exitCode;
public String getStdout() {
return stdout;
}
public void setStdout(String stdout) {
this.stdout = stdout;
}
public String getStderr() {
return stderr;
}
public void setStderr(String stderr) {
this.stderr = stderr;
}
public String getExitCode() {
return exitCode;
}
public void setExitCode(String exitCode) {
this.exitCode = exitCode;
}
}

View File

@ -0,0 +1,29 @@
//
// 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.diagnostics;
import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd;
import java.util.Map;
public interface DiagnosticsService {
Map<String, String> runDiagnosticsCommand(RunDiagnosticsCmd cmd);
}

View File

@ -0,0 +1,42 @@
//
// 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.diagnostics;
public enum DiagnosticsType {
PING("ping"), TRACEROUTE("traceroute"), ARPING("arping");
private String value;
DiagnosticsType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static DiagnosticsType getCommand(String cmd) {
for (DiagnosticsType type : DiagnosticsType.values()) {
if (type.value.equalsIgnoreCase(cmd)) {
return type;
}
}
return null;
}
}

View File

@ -69,4 +69,5 @@ public class VRScripts {
public static final String VR_CFG = "vr_cfg.sh";
public static final String DIAGNOSTICS = "diagnostics.py";
}

View File

@ -22,6 +22,9 @@ package com.cloud.agent.resource.virtualnetwork;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import org.apache.cloudstack.diagnostics.DiagnosticsAnswer;
import org.apache.cloudstack.diagnostics.DiagnosticsCommand;
import org.joda.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
@ -189,9 +192,11 @@ public class VirtualRoutingResource {
} else if (cmd instanceof GetDomRVersionCmd) {
return execute((GetDomRVersionCmd)cmd);
} else if (cmd instanceof CheckS2SVpnConnectionsCommand) {
return execute((CheckS2SVpnConnectionsCommand) cmd);
return execute((CheckS2SVpnConnectionsCommand)cmd);
} else if (cmd instanceof GetRouterAlertsCommand) {
return execute((GetRouterAlertsCommand)cmd);
} else if (cmd instanceof DiagnosticsCommand) {
return execute((DiagnosticsCommand)cmd);
} else {
s_logger.error("Unknown query command in VirtualRoutingResource!");
return Answer.createUnsupportedCommandAnswer(cmd);
@ -292,6 +297,15 @@ public class VirtualRoutingResource {
return new CheckRouterAnswer(cmd, result.getDetails(), true);
}
private Answer execute(DiagnosticsCommand cmd) {
_eachTimeout = Duration.standardSeconds(NumbersUtil.parseInt("60", 60));
final ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.DIAGNOSTICS, cmd.getSrciptArguments(), _eachTimeout);
if (!result.isSuccess()) {
return new DiagnosticsAnswer(cmd, false, result.getDetails());
}
return new DiagnosticsAnswer(cmd, result.isSuccess(), result.getDetails());
}
private Answer execute(GetDomRVersionCmd cmd) {
final ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.VERSION, null);
if (!result.isSuccess()) {
@ -454,6 +468,6 @@ public class VirtualRoutingResource {
_vrAggregateCommandsSet.remove(routerName);
}
}
return new Answer(cmd, false, "Fail to recongize aggregation action " + action.toString());
return new Answer(cmd, false, "Fail to recognize aggregation action " + action.toString());
}
}

View File

@ -0,0 +1,54 @@
// 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.diagnostics;
import com.cloud.agent.api.Answer;
import com.cloud.utils.exception.CloudRuntimeException;
import com.google.common.base.Strings;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
public class DiagnosticsAnswer extends Answer {
public static final Logger LOGGER = Logger.getLogger(DiagnosticsAnswer.class);
public DiagnosticsAnswer(DiagnosticsCommand cmd, boolean result, String details) {
super(cmd, result, details);
}
public Map<String, String> getExecutionDetails() {
final Map<String, String> executionDetailsMap = new HashMap<>();
if (result == true && !Strings.isNullOrEmpty(details)) {
final String[] parseDetails = details.split("&&");
if (parseDetails.length >= 3) {
executionDetailsMap.put(ApiConstants.STDOUT, parseDetails[0].trim());
executionDetailsMap.put(ApiConstants.STDERR, parseDetails[1].trim());
executionDetailsMap.put(ApiConstants.EXITCODE, String.valueOf(parseDetails[2]).trim());
} else {
throw new CloudRuntimeException("Unsupported diagnostics command type supplied");
}
} else {
executionDetailsMap.put(ApiConstants.STDOUT, "");
executionDetailsMap.put(ApiConstants.STDERR, details);
executionDetailsMap.put(ApiConstants.EXITCODE, "-1");
}
return executionDetailsMap;
}
}

View File

@ -0,0 +1,44 @@
// 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.diagnostics;
import com.cloud.agent.api.routing.NetworkElementCommand;
public class DiagnosticsCommand extends NetworkElementCommand {
private final String scriptArguments;
private final boolean executeInSequence;
public DiagnosticsCommand(String scriptArguments, boolean executeInSequence) {
this.scriptArguments = scriptArguments;
this.executeInSequence = executeInSequence;
}
public String getSrciptArguments() {
return scriptArguments;
}
@Override
public boolean isQuery() {
return true;
}
@Override
public boolean executeInSequence() {
return this.executeInSequence;
}
}

View File

@ -33,6 +33,7 @@ import com.cloud.agent.api.PingTestCommand;
import com.cloud.resource.AgentResourceBase;
import com.cloud.simulator.MockHost;
import com.cloud.utils.component.Manager;
import org.apache.cloudstack.diagnostics.DiagnosticsCommand;
public interface MockAgentManager extends Manager {
public static final long DEFAULT_HOST_MEM_SIZE = 8 * 1024 * 1024 * 1024L; // 8G, unit of Mbytes
@ -64,4 +65,6 @@ public interface MockAgentManager extends Manager {
Answer maintain(MaintainCommand cmd);
Answer checkNetworkCommand(CheckNetworkCommand cmd);
Answer runDiagnostics(DiagnosticsCommand cmd);
}

View File

@ -16,29 +16,6 @@
// under the License.
package com.cloud.agent.manager;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.PatternSyntaxException;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.ca.SetupCertificateAnswer;
import org.apache.cloudstack.ca.SetupCertificateCommand;
import org.apache.cloudstack.ca.SetupKeyStoreCommand;
import org.apache.cloudstack.ca.SetupKeystoreAnswer;
import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckHealthCommand;
@ -49,6 +26,7 @@ import com.cloud.agent.api.GetHostStatsCommand;
import com.cloud.agent.api.HostStatsEntry;
import com.cloud.agent.api.MaintainAnswer;
import com.cloud.agent.api.PingTestCommand;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.api.commands.SimulatorAddSecondaryAgent;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.exception.DiscoveryException;
@ -73,6 +51,29 @@ import com.cloud.utils.db.DB;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import org.apache.cloudstack.ca.SetupCertificateAnswer;
import org.apache.cloudstack.ca.SetupCertificateCommand;
import org.apache.cloudstack.ca.SetupKeyStoreCommand;
import org.apache.cloudstack.ca.SetupKeystoreAnswer;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.diagnostics.DiagnosticsAnswer;
import org.apache.cloudstack.diagnostics.DiagnosticsCommand;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.PatternSyntaxException;
@Component
public class MockAgentManagerImpl extends ManagerBase implements MockAgentManager {
@ -481,6 +482,7 @@ public class MockAgentManagerImpl extends ManagerBase implements MockAgentManage
return new SetupCertificateAnswer(true);
}
@Override
public boolean start() {
for (Discoverer discoverer : discoverers) {
@ -520,6 +522,14 @@ public class MockAgentManagerImpl extends ManagerBase implements MockAgentManage
return new CheckNetworkAnswer(cmd, true, "Network Setup check by names is done");
}
@Override
public Answer runDiagnostics(final DiagnosticsCommand cmd) {
final String vmInstance = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
final String[] args = cmd.getSrciptArguments().split(" ");
final String mockAnswer = String.format("%s %s executed in %s && && 0", args[0].toUpperCase(), args[1], vmInstance);
return new DiagnosticsAnswer(cmd, true, mockAnswer);
}
public List<Discoverer> getDiscoverers() {
return discoverers;
}

View File

@ -26,6 +26,7 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.diagnostics.DiagnosticsCommand;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@ -293,7 +294,9 @@ public class SimulatorManagerImpl extends ManagerBase implements SimulatorManage
} else if (cmd instanceof PingTestCommand) {
answer = _mockAgentMgr.pingTest((PingTestCommand)cmd);
} else if (cmd instanceof SetupKeyStoreCommand) {
answer = _mockAgentMgr.setupKeyStore((SetupKeyStoreCommand)cmd);
answer = _mockAgentMgr.setupKeyStore((SetupKeyStoreCommand) cmd);
}else if (cmd instanceof DiagnosticsCommand) {
answer = _mockAgentMgr.runDiagnostics((DiagnosticsCommand)cmd);
} else if (cmd instanceof SetupCertificateCommand) {
answer = _mockAgentMgr.setupCertificate((SetupCertificateCommand)cmd);
} else if (cmd instanceof PrepareForMigrationCommand) {

View File

@ -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.
package org.apache.cloudstack.diagnostics;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.component.PluggableService;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.VMInstanceDao;
import com.google.common.base.Strings;
import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.log4j.Logger;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class DiagnosticsServiceImpl extends ManagerBase implements PluggableService, DiagnosticsService {
private static final Logger LOGGER = Logger.getLogger(DiagnosticsServiceImpl.class);
@Inject
private AgentManager agentManager;
@Inject
private VMInstanceDao instanceDao;
@Inject
private VirtualMachineManager vmManager;
@Inject
private NetworkOrchestrationService networkManager;
@Override
public Map<String, String> runDiagnosticsCommand(final RunDiagnosticsCmd cmd) {
final Long vmId = cmd.getId();
final String cmdType = cmd.getType().getValue();
final String ipAddress = cmd.getAddress();
final String optionalArguments = cmd.getOptionalArguments();
final VMInstanceVO vmInstance = instanceDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.DomainRouter, VirtualMachine.Type.SecondaryStorageVm);
if (vmInstance == null) {
throw new InvalidParameterValueException("Unable to find a system vm with id " + vmId);
}
final Long hostId = vmInstance.getHostId();
if (hostId == null) {
throw new CloudRuntimeException("Unable to find host for virtual machine instance: " + vmInstance.getInstanceName());
}
final String shellCmd = prepareShellCmd(cmdType, ipAddress, optionalArguments);
if (Strings.isNullOrEmpty(shellCmd)) {
throw new IllegalArgumentException("Optional parameters contain unwanted characters: " + optionalArguments);
}
final Hypervisor.HypervisorType hypervisorType = vmInstance.getHypervisorType();
final DiagnosticsCommand command = new DiagnosticsCommand(shellCmd, vmManager.getExecuteInSequence(hypervisorType));
final Map<String, String> accessDetails = networkManager.getSystemVMAccessDetails(vmInstance);
if (Strings.isNullOrEmpty(accessDetails.get(NetworkElementCommand.ROUTER_IP))) {
throw new CloudRuntimeException("Unable to set system vm ControlIP for system vm with ID: " + vmId);
}
command.setAccessDetail(accessDetails);
Map<String, String> detailsMap;
final Answer answer = agentManager.easySend(hostId, command);
if (answer != null && (answer instanceof DiagnosticsAnswer)) {
detailsMap = ((DiagnosticsAnswer) answer).getExecutionDetails();
return detailsMap;
} else {
throw new CloudRuntimeException("Failed to execute diagnostics command on remote host: " + answer.getDetails());
}
}
protected boolean hasValidChars(String optionalArgs) {
if (Strings.isNullOrEmpty(optionalArgs)) {
return true;
} else {
final String regex = "^[\\w\\-\\s.]+$";
final Pattern pattern = Pattern.compile(regex);
return pattern.matcher(optionalArgs).find();
}
}
protected String prepareShellCmd(String cmdType, String ipAddress, String optionalParams) {
final String CMD_TEMPLATE = String.format("%s %s", cmdType, ipAddress);
if (Strings.isNullOrEmpty(optionalParams)) {
return CMD_TEMPLATE;
} else {
if (hasValidChars(optionalParams)) {
return String.format("%s %s", CMD_TEMPLATE, optionalParams);
} else {
return null;
}
}
}
@Override
public List<Class<?>> getCommands() {
List<Class<?>> cmdList = new ArrayList<>();
cmdList.add(RunDiagnosticsCmd.class);
return cmdList;
}
}

View File

@ -298,4 +298,6 @@
<bean id="indirectAgentLBService" class="org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl" />
<bean id="directDownloadManager" class="org.apache.cloudstack.direct.download.DirectDownloadManagerImpl" />
<bean id="DiagnosticsService" class="org.apache.cloudstack.diagnostics.DiagnosticsServiceImpl" />
</beans>

View File

@ -0,0 +1,196 @@
//
// 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.diagnostics;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.VMInstanceDao;
import junit.framework.TestCase;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.command.admin.diagnostics.RunDiagnosticsCmd;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.HashMap;
import java.util.Map;
@RunWith(MockitoJUnitRunner.class)
public class DiagnosticsServiceImplTest extends TestCase {
@Mock
private AgentManager agentManager;
@Mock
private VMInstanceDao instanceDao;
@Mock
private RunDiagnosticsCmd diagnosticsCmd;
@Mock
private DiagnosticsCommand command;
@Mock
private VMInstanceVO instanceVO;
@Mock
private VirtualMachineManager vmManager;
@Mock
private NetworkOrchestrationService networkManager;
@InjectMocks
private DiagnosticsServiceImpl diagnosticsService = new DiagnosticsServiceImpl();
@Before
public void setUp() throws Exception {
Mockito.when(diagnosticsCmd.getId()).thenReturn(1L);
Mockito.when(diagnosticsCmd.getType()).thenReturn(DiagnosticsType.PING);
Mockito.when(instanceDao.findByIdTypes(Mockito.anyLong(), Mockito.any(VirtualMachine.Type.class),
Mockito.any(VirtualMachine.Type.class), Mockito.any(VirtualMachine.Type.class))).thenReturn(instanceVO);
}
@After
public void tearDown() throws Exception {
Mockito.reset(diagnosticsCmd);
Mockito.reset(agentManager);
Mockito.reset(instanceDao);
Mockito.reset(instanceVO);
Mockito.reset(command);
}
@Test
public void testRunDiagnosticsCommandTrue() throws Exception {
Mockito.when(diagnosticsCmd.getAddress()).thenReturn("8.8.8.8");
Map<String, String> accessDetailsMap = new HashMap<>();
accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, "169.20.175.10");
Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap);
final String details = "PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n" +
"64 bytes from 8.8.8.8: icmp_seq=1 ttl=125 time=7.88 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=2 ttl=125 time=251 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=3 ttl=125 time=64.9 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=4 ttl=125 time=50.7 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=5 ttl=125 time=67.9 ms\n" +
"\n" +
"--- 8.8.8.8 ping statistics ---\n" +
"5 packets transmitted, 5 received, 0% packet loss, time 4003ms\n" +
"rtt min/avg/max/mdev = 7.881/88.587/251.410/84.191 ms&&\n" +
"&&\n" +
"0\n";
Mockito.when(agentManager.easySend(Mockito.anyLong(), Mockito.any(DiagnosticsCommand.class))).thenReturn(new DiagnosticsAnswer(command, true, details));
Map<String, String> detailsMap = diagnosticsService.runDiagnosticsCommand(diagnosticsCmd);
String stdout = "PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.\n" +
"64 bytes from 8.8.8.8: icmp_seq=1 ttl=125 time=7.88 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=2 ttl=125 time=251 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=3 ttl=125 time=64.9 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=4 ttl=125 time=50.7 ms\n" +
"64 bytes from 8.8.8.8: icmp_seq=5 ttl=125 time=67.9 ms\n" +
"\n" +
"--- 8.8.8.8 ping statistics ---\n" +
"5 packets transmitted, 5 received, 0% packet loss, time 4003ms\n" +
"rtt min/avg/max/mdev = 7.881/88.587/251.410/84.191 ms";
assertEquals(3, detailsMap.size());
assertEquals("Mismatch between actual and expected STDERR", "", detailsMap.get(ApiConstants.STDERR));
assertEquals("Mismatch between actual and expected EXITCODE", "0", detailsMap.get(ApiConstants.EXITCODE));
assertEquals("Mismatch between actual and expected STDOUT", stdout, detailsMap.get(ApiConstants.STDOUT));
}
@Test
public void testRunDiagnosticsCommandFalse() throws Exception {
Mockito.when(diagnosticsCmd.getAddress()).thenReturn("192.0.2.2");
Map<String, String> accessDetailsMap = new HashMap<>();
accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, "169.20.175.10");
Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap);
String details = "PING 192.0.2.2 (192.0.2.2): 56 data bytes\n" +
"76 bytes from 213.130.48.253: Destination Net Unreachable\n" +
"--- 192.0.2.2 ping statistics ---\n" +
"4 packets transmitted, 0 packets received, 100% packet loss&&\n" +
"&&\n" +
"1\n";
String stdout = "PING 192.0.2.2 (192.0.2.2): 56 data bytes\n" +
"76 bytes from 213.130.48.253: Destination Net Unreachable\n" +
"--- 192.0.2.2 ping statistics ---\n" +
"4 packets transmitted, 0 packets received, 100% packet loss";
Mockito.when(agentManager.easySend(Mockito.anyLong(), Mockito.any(DiagnosticsCommand.class))).thenReturn(new DiagnosticsAnswer(command, true, details));
Map<String, String> detailsMap = diagnosticsService.runDiagnosticsCommand(diagnosticsCmd);
assertEquals(3, detailsMap.size());
assertEquals("Mismatch between actual and expected STDERR", "", detailsMap.get(ApiConstants.STDERR));
assertTrue("Mismatch between actual and expected EXITCODE", !detailsMap.get(ApiConstants.EXITCODE).equalsIgnoreCase("0"));
assertEquals("Mismatch between actual and expected STDOUT", stdout, detailsMap.get(ApiConstants.STDOUT));
}
@Test(expected = InvalidParameterValueException.class)
public void testRunDiagnosticsThrowsInvalidParamException() throws Exception {
Mockito.when(diagnosticsCmd.getAddress()).thenReturn("");
Mockito.when(instanceDao.findByIdTypes(Mockito.anyLong(), Mockito.any(VirtualMachine.Type.class),
Mockito.any(VirtualMachine.Type.class), Mockito.any(VirtualMachine.Type.class))).thenReturn(null);
diagnosticsService.runDiagnosticsCommand(diagnosticsCmd);
}
@Test(expected = CloudRuntimeException.class)
public void testVMControlIPisNull() throws Exception {
Mockito.when(diagnosticsCmd.getAddress()).thenReturn("0.42.42.42");
Map<String, String> accessDetailsMap = new HashMap<>();
accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, null);
Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap);
diagnosticsService.runDiagnosticsCommand(diagnosticsCmd);
}
@Test
public void testInvalidCharsInParams() throws Exception {
assertFalse(diagnosticsService.hasValidChars("'\\''"));
assertFalse(diagnosticsService.hasValidChars("-I eth0 &"));
assertFalse(diagnosticsService.hasValidChars("-I eth0 ;"));
assertFalse(diagnosticsService.hasValidChars(" &2 > "));
assertFalse(diagnosticsService.hasValidChars(" &2 >> "));
assertFalse(diagnosticsService.hasValidChars(" | "));
assertFalse(diagnosticsService.hasValidChars("|"));
assertFalse(diagnosticsService.hasValidChars(","));
}
@Test
public void testValidCharsInParams() throws Exception {
assertTrue(diagnosticsService.hasValidChars(""));
assertTrue(diagnosticsService.hasValidChars("."));
assertTrue(diagnosticsService.hasValidChars(" "));
assertTrue(diagnosticsService.hasValidChars("-I eth0 www.google.com"));
assertTrue(diagnosticsService.hasValidChars(" "));
assertTrue(diagnosticsService.hasValidChars(" -I cloudbr0 --sport "));
assertTrue(diagnosticsService.hasValidChars(" --back -m20 "));
assertTrue(diagnosticsService.hasValidChars("-c 5 -4"));
assertTrue(diagnosticsService.hasValidChars("-c 5 -4 -AbDfhqUV"));
}
}

View File

@ -0,0 +1,71 @@
#!/usr/bin/env python
# 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.
import shlex
import subprocess
import sys
def run_cmd(command):
if command is not None:
try:
p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
return_code = p.returncode
except OSError as e:
stdout = "Check your command type"
stderr = "Exception occurred: %s" % e
return_code = 1
finally:
print('%s&&' % stdout.strip())
print('%s&&' % stderr.strip())
print('%s' % return_code)
def get_command():
arguments = sys.argv
cmd = " ".join(arguments[1:])
cmd_type = sys.argv[1]
if cmd_type == 'ping':
if '-c' in arguments:
return cmd
else:
return cmd + " -c 4"
elif cmd_type == 'traceroute':
if '-m' in arguments:
return cmd
else:
return cmd + " -m 20"
elif cmd_type == 'arping':
if '-c' in arguments:
return cmd
else:
return cmd + " -c 4"
else:
return None
if __name__ == "__main__":
command = get_command()
run_cmd(command)

View File

@ -0,0 +1,539 @@
# 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.
""" BVT tests for remote diagnostics of system VMs
"""
# Import Local Modules
from marvin.codes import FAILED
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.cloudstackAPI import runDiagnostics
from marvin.lib.utils import (cleanup_resources)
from marvin.lib.base import (Account,
ServiceOffering,
VirtualMachine)
from marvin.lib.common import (get_domain,
get_zone,
get_test_template,
list_ssvms,
list_routers)
from nose.plugins.attrib import attr
class TestRemoteDiagnostics(cloudstackTestCase):
"""
Test remote diagnostics with system VMs and VR as root admin
"""
@classmethod
def setUpClass(cls):
testClient = super(TestRemoteDiagnostics, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.hypervisor = testClient.getHypervisorInfo()
cls.services['mode'] = cls.zone.networktype
template = get_test_template(
cls.apiclient,
cls.zone.id,
cls.hypervisor
)
if template == FAILED:
cls.fail("get_test_template() failed to return template")
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
# Create an account, network, VM and IP addresses
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
domainid=cls.domain.id
)
cls.service_offering = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["tiny"]
)
cls.vm_1 = VirtualMachine.create(
cls.apiclient,
cls.services["virtual_machine"],
templateid=template.id,
accountid=cls.account.name,
domainid=cls.account.domainid,
serviceofferingid=cls.service_offering.id
)
cls.cleanup = [
cls.account,
cls.service_offering
]
@classmethod
def tearDownClass(cls):
try:
cls.apiclient = super(
TestRemoteDiagnostics,
cls
).getClsTestClient().getApiClient()
# Clean up, terminate the created templates
cleanup_resources(cls.apiclient, cls.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.hypervisor = self.testClient.getHypervisorInfo()
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_01_ping_in_vr_success(self):
'''
Test Ping command execution in VR
'''
# Validate the following:
# 1. Ping command is executed remotely on VR
list_router_response = list_routers(
self.apiclient,
account=self.account.name,
domainid=self.account.domainid
)
self.assertEqual(
isinstance(list_router_response, list),
True,
"Check list response returns a valid list"
)
router = list_router_response[0]
self.debug('Starting the router with ID: %s' % router.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = router.id
cmd.ipaddress = '8.8.8.8'
cmd.type = 'ping'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Ping in VR')
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_02_ping_in_vr_failure(self):
'''
Test Ping command execution in VR
'''
# Validate the following:
# 1. Ping command is executed remotely on VR
# 2. Validate Ping command execution with a non-existent/pingable IP address
if self.hypervisor.lower() == 'simulator':
raise self.skipTest("Skipping negative test case for Simulator hypervisor")
list_router_response = list_routers(
self.apiclient,
account=self.account.name,
domainid=self.account.domainid
)
self.assertEqual(
isinstance(list_router_response, list),
True,
"Check list response returns a valid list"
)
router = list_router_response[0]
self.debug('Starting the router with ID: %s' % router.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = router.id
cmd.ipaddress = '192.0.2.2'
cmd.type = 'ping'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertNotEqual(
'0',
cmd_response.exitcode,
'Check diagnostics command returns a non-zero exit code')
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_03_ping_in_ssvm_success(self):
'''
Test Ping command execution in SSVM
'''
# Validate the following:
# 1. Ping command is executed remotely on SSVM
list_ssvm_response = list_ssvms(
self.apiclient,
systemvmtype='secondarystoragevm',
state='Running',
)
self.assertEqual(
isinstance(list_ssvm_response, list),
True,
'Check list response returns a valid list'
)
ssvm = list_ssvm_response[0]
self.debug('Setting up SSVM with ID %s' % ssvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = ssvm.id
cmd.ipaddress = '8.8.8.8'
cmd.type = 'ping'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Ping in SSVM'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_04_ping_in_ssvm_failure(self):
'''
Test Ping command execution in SSVM
'''
# Validate the following:
# 1. Ping command is executed remotely on SSVM
# 2. Validate Ping command execution with a non-existent/pingable IP address
if self.hypervisor.lower() == 'simulator':
raise self.skipTest("Skipping negative test case for Simulator hypervisor")
list_ssvm_response = list_ssvms(
self.apiclient,
systemvmtype='secondarystoragevm',
state='Running',
)
self.assertEqual(
isinstance(list_ssvm_response, list),
True,
'Check list response returns a valid list'
)
ssvm = list_ssvm_response[0]
self.debug('Setting up SSVM with ID %s' % ssvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = ssvm.id
cmd.ipaddress = '192.0.2.2'
cmd.type = 'ping'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertNotEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Ping in SSVM'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_05_ping_in_cpvm_success(self):
'''
Test Ping command execution in CPVM
'''
# Validate the following:
# 1. Ping command is executed remotely on CPVM
list_ssvm_response = list_ssvms(
self.apiclient,
systemvmtype='consoleproxy',
state='Running',
)
self.assertEqual(
isinstance(list_ssvm_response, list),
True,
'Check list response returns a valid list'
)
cpvm = list_ssvm_response[0]
self.debug('Setting up CPVM with ID %s' % cpvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = cpvm.id
cmd.ipaddress = '8.8.8.8'
cmd.type = 'ping'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Ping in CPVM'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_06_ping_in_cpvm_failure(self):
'''
Test Ping command execution in CPVM
'''
# Validate the following:
# 1. Ping command is executed remotely on CPVM
# 2. Validate Ping command execution with a non-existent/pingable IP address
if self.hypervisor.lower() == 'simulator':
raise self.skipTest("Skipping negative test case for Simulator hypervisor")
list_ssvm_response = list_ssvms(
self.apiclient,
systemvmtype='consoleproxy',
state='Running',
)
self.assertEqual(
isinstance(list_ssvm_response, list),
True,
'Check list response returns a valid list'
)
cpvm = list_ssvm_response[0]
self.debug('Setting up CPVM with ID %s' % cpvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = cpvm.id
cmd.ipaddress = '192.0.2.2'
cmd.type = 'ping'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertNotEqual(
'0',
cmd_response.exitcode,
'Check diagnostics command returns a non-zero exit code'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_07_arping_in_vr(self):
'''
Test Arping command execution in VR
'''
# Validate the following:
# 1. Arping command is executed remotely on VR
list_router_response = list_routers(
self.apiclient,
account=self.account.name,
domainid=self.account.domainid
)
self.assertEqual(
isinstance(list_router_response, list),
True,
"Check list response returns a valid list"
)
router = list_router_response[0]
self.debug('Starting the router with ID: %s' % router.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = router.id
cmd.ipaddress = router.gateway
cmd.type = 'arping'
cmd.params = "-I eth2"
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Arping in VR')
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_08_arping_in_ssvm(self):
'''
Test Arping command execution in SSVM
'''
# Validate the following:
# 1. Arping command is executed remotely on SSVM
list_ssvm_response = list_ssvms(
self.apiclient,
systemvmtype='secondarystoragevm',
state='Running',
)
self.assertEqual(
isinstance(list_ssvm_response, list),
True,
'Check list response returns a valid list'
)
ssvm = list_ssvm_response[0]
self.debug('Setting up SSVM with ID %s' % ssvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = ssvm.id
cmd.ipaddress = ssvm.gateway
cmd.type = 'arping'
cmd.params = '-I eth2'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Arping in SSVM'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_09_arping_in_cpvm(self):
'''
Test Arping command execution in CPVM
'''
# Validate the following:
# 1. Arping command is executed remotely on CPVM
list_cpvm_response = list_ssvms(
self.apiclient,
systemvmtype='secondarystoragevm',
state='Running',
)
self.assertEqual(
isinstance(list_cpvm_response, list),
True,
'Check list response returns a valid list'
)
cpvm = list_cpvm_response[0]
self.debug('Setting up CPVM with ID %s' % cpvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = cpvm.id
cmd.ipaddress = cpvm.gateway
cmd.type = 'arping'
cmd.params = '-I eth2'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Arping in CPVM'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_10_traceroute_in_vr(self):
'''
Test Arping command execution in VR
'''
# Validate the following:
# 1. Arping command is executed remotely on VR
list_router_response = list_routers(
self.apiclient,
account=self.account.name,
domainid=self.account.domainid
)
self.assertEqual(
isinstance(list_router_response, list),
True,
"Check list response returns a valid list"
)
router = list_router_response[0]
self.debug('Starting the router with ID: %s' % router.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = router.id
cmd.ipaddress = '8.8.4.4'
cmd.type = 'traceroute'
cmd.params = "-m 10"
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Arping in VR')
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_11_traceroute_in_ssvm(self):
'''
Test Traceroute command execution in SSVM
'''
# Validate the following:
# 1. Traceroute command is executed remotely on SSVM
list_ssvm_response = list_ssvms(
self.apiclient,
systemvmtype='secondarystoragevm',
state='Running',
)
self.assertEqual(
isinstance(list_ssvm_response, list),
True,
'Check list response returns a valid list'
)
ssvm = list_ssvm_response[0]
self.debug('Setting up SSVM with ID %s' % ssvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = ssvm.id
cmd.ipaddress = '8.8.4.4'
cmd.type = 'traceroute'
cmd.params = '-m 10'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Traceroute in SSVM'
)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="true")
def test_12_traceroute_in_cpvm(self):
'''
Test Traceroute command execution in CPVMM
'''
# Validate the following:
# 1. Traceroute command is executed remotely on CPVM
list_cpvm_response = list_ssvms(
self.apiclient,
systemvmtype='consoleproxy',
state='Running',
)
self.assertEqual(
isinstance(list_cpvm_response, list),
True,
'Check list response returns a valid list'
)
cpvm = list_cpvm_response[0]
self.debug('Setting up CPVMM with ID %s' % cpvm.id)
cmd = runDiagnostics.runDiagnosticsCmd()
cmd.targetid = cpvm.id
cmd.ipaddress = '8.8.4.4'
cmd.type = 'traceroute'
cmd.params = '-m 10'
cmd_response = self.apiclient.runDiagnostics(cmd)
self.assertEqual(
'0',
cmd_response.exitcode,
'Failed to run remote Traceroute in CPVM'
)

View File

@ -190,7 +190,8 @@ known_categories = {
'CA': 'Certificate',
'listElastistorInterface': 'Misc',
'cloudian': 'Cloudian',
'Sioc' : 'Sioc'
'Sioc' : 'Sioc',
'Diagnostics': 'Diagnostics'
}