Skip to content
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0658e24
Beginning to End workflow for Virtual Router
May 15, 2018
bbe380d
Refactoring unused code
May 17, 2018
836bcb1
Testing agent-manager connection error
May 21, 2018
67669db
Minor Fixes on Command/Answer class
May 24, 2018
95e8e57
Initial successful test with VR and Ping command
May 28, 2018
17dc1e1
Modified code to allow for all system VMs and all three ICMP commands
May 30, 2018
8cf67d3
Fixing typos
May 31, 2018
c9d4458
Fixing merge conflicts
May 31, 2018
259a459
Migrating to python based script
May 31, 2018
cd5e623
Renaming packages to diagnostics
May 31, 2018
bf01218
Removing nested comments
Jun 1, 2018
448fb44
Working on unit test impl
Jun 1, 2018
1e60bdc
Migrated to python based script, and added validation for optional pa…
Jun 5, 2018
9b9cb89
Removing unsused imports
Jun 5, 2018
72f7023
Adding Marvin tests
Jun 5, 2018
05483de
Adding traceroute command to Marvin tests
Jun 6, 2018
1df8855
Renaming packages to diagnostics and code refactoring
Jun 11, 2018
1ff1298
Refactoring API response object to include STDOUT, STDERR and EXITCODE
Jun 12, 2018
6a09386
Marvin tests complete
Jun 12, 2018
0c45874
Adding unit tests
Jun 13, 2018
289a4ed
For rebuilding testing packages
Jun 14, 2018
e3f2bef
Using AgentManager's EasySend
Jun 18, 2018
7645c07
Testing VMware hypervisor
Jun 18, 2018
9801b9f
Removing unused import
Jun 18, 2018
149f422
Fix issue with packaging failures
Jun 18, 2018
97e560d
Setting executeInSequence for VMware tests
Jun 18, 2018
50d152f
For testing VMware fix
Jun 19, 2018
93a3cfb
Fix typos in Answer class
Jun 19, 2018
5571123
Testing Fix for system vm access details
Jun 20, 2018
6a11767
Removed commented code
Jun 20, 2018
2ddb21b
Adding fix for VMware and ammending unit tests to capture changes
Jun 20, 2018
11a818b
Removed hard-coded values in Cmd class
Jun 21, 2018
2f5767c
Changed API name to runDiagnostics
Jun 22, 2018
80058c2
Updated marvin and unit test to use new API name, also changed from i…
Jun 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// 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.ExecuteDiagnosticsResponse;
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.log4j.Logger;

import javax.inject.Inject;
import java.util.Map;
import java.util.regex.Pattern;

@APICommand(name = ExecuteDiagnosticsCmd.APINAME, responseObject = ExecuteDiagnosticsResponse.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 ExecuteDiagnosticsCmd extends BaseCmd {
private static final Logger LOGGER = Logger.getLogger(ExecuteDiagnosticsCmd.class);
public static final String APINAME = "executeDiagnostics";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you consider renaming this to runDiagnostics. Ignore if already discussed with Paul and others.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point, might be a bit late. It would require redoing and testing a lot of things i.e unit/marvin test cases etc.


@Inject
private DiagnosticsService diagnosticsService;

/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.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() {
final String EMPTY_STRING = "";

if (optionalArguments == null || optionalArguments.isEmpty()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do this a lot as well, but it's better to use CollectionUtils.isEmpty()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not much difference in terms of performance, but adopted since it provides cleaner code

return EMPTY_STRING;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return "" is fine, than declare a variable and return that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
final String regex = "^[\\w\\-\\s]+$";
final Pattern pattern = Pattern.compile(regex);
final boolean hasInvalidChar = pattern.matcher(optionalArguments).find();
if (!hasInvalidChar) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write +/- unit tests for this validation method.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

throw new IllegalArgumentException("Optional parameters contain unwanted characters: " + optionalArguments);
}
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 {
ExecuteDiagnosticsResponse response = new ExecuteDiagnosticsResponse();
try {
final Map<String, String> answerMap = diagnosticsService.runDiagnosticsCommand(this);
if (answerMap != null || !answerMap.isEmpty()) {
response.setStdout(answerMap.get("STDOUT"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the .get() to get based on a known constant than a hardcoded string like STDOUT, STDERR and EXITCODE.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

response.setStderr(answerMap.get("STDERR"));
response.setExitCode(answerMap.get("EXITCODE"));
response.setObjectName("diagnostics results");
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
} catch (ServerApiException e) {
LOGGER.error("Exception occurred while executing remote diagnostics command: ", e);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and wrapping a server exception with only INTERNAL_ERROR as message doesn't add much value.

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// 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.BaseResponse;
import org.apache.cloudstack.api.EntityReference;

@EntityReference(value = VirtualMachine.class)
public class ExecuteDiagnosticsResponse extends BaseResponse {
@SerializedName("STDOUT")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lowercase the api response keys to stdout, stderr, exitcode.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use api constant class for them, perhaps use the same keys in the class where the details map is created.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@Param(description = "the standard output from the command execution")
private String stdout;

@SerializedName("STDERR")
@Param(description = "the standard error output from the command execution")
private String stderr;

@SerializedName("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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// 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.exception.AgentUnavailableException;
import com.cloud.exception.InvalidParameterValueException;
import org.apache.cloudstack.api.command.admin.diagnostics.ExecuteDiagnosticsCmd;

import java.util.Map;

public interface DiagnosticsService {

Map<String, String> runDiagnosticsCommand(ExecuteDiagnosticsCmd cmd) throws AgentUnavailableException, InvalidParameterValueException;

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ public class VRScripts {

public static final String VR_CFG = "vr_cfg.sh";

// New script for use with Remote Diagnostics API

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove comment.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

public static final String DIAGNOSTICS = "diagnostics.py";
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,6 @@

package com.cloud.agent.resource.virtualnetwork;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import org.joda.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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.utils.security.KeyStoreUtils;
import org.apache.log4j.Logger;

import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckRouterAnswer;
import com.cloud.agent.api.CheckRouterCommand;
Expand All @@ -59,6 +36,29 @@
import com.cloud.utils.ExecutionResult;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.exception.CloudRuntimeException;
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.diagnostics.DiagnosticsAnswer;
import org.apache.cloudstack.diagnostics.DiagnosticsCommand;
import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.log4j.Logger;
import org.joda.time.Duration;

import javax.naming.ConfigurationException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* VirtualNetworkResource controls and configures virtual networking
Expand Down Expand Up @@ -185,13 +185,15 @@ private Answer execute(final SetupCertificateCommand cmd) {

private Answer executeQueryCommand(NetworkElementCommand cmd) {
if (cmd instanceof CheckRouterCommand) {
return execute((CheckRouterCommand)cmd);
return execute((CheckRouterCommand) cmd);
} else if (cmd instanceof GetDomRVersionCmd) {
return execute((GetDomRVersionCmd)cmd);
return execute((GetDomRVersionCmd) cmd);
} else if (cmd instanceof CheckS2SVpnConnectionsCommand) {
return execute((CheckS2SVpnConnectionsCommand) cmd);
} else if (cmd instanceof GetRouterAlertsCommand) {
return execute((GetRouterAlertsCommand)cmd);
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);
Expand Down Expand Up @@ -292,6 +294,15 @@ private Answer execute(CheckRouterCommand cmd) {
return new CheckRouterAnswer(cmd, result.getDetails(), true);
}

private Answer execute(DiagnosticsCommand cmd) {
_eachTimeout = Duration.standardSeconds(NumbersUtil.parseInt("60",60));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space in 60", 60.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved

final ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.DIAGNOSTICS, cmd.getSrciptArguments(), _eachTimeout);
if (!result.isSuccess()) {
return new DiagnosticsAnswer(cmd, result.isSuccess(), "Diagnostics Command Execution failed: " + 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()) {
Expand Down
Loading