diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 907b93eca103..51f13f3bb87b 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -16,9 +16,6 @@ // under the License. package com.cloud.event; -import java.util.HashMap; -import java.util.Map; - import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.dc.StorageNetworkIpRange; @@ -76,6 +73,9 @@ import org.apache.cloudstack.ha.HAConfig; import org.apache.cloudstack.usage.Usage; +import java.util.HashMap; +import java.util.Map; + public class EventTypes { //map of Event and corresponding entity for which Event is applicable @@ -96,6 +96,7 @@ public class EventTypes { public static final String EVENT_VM_MOVE = "VM.MOVE"; public static final String EVENT_VM_RESTORE = "VM.RESTORE"; public static final String EVENT_VM_EXPUNGE = "VM.EXPUNGE"; + public static final String EVENT_VM_DIAGNOSTICS = "VM.DIAGNOSTICS"; // Domain Router public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE"; @@ -604,6 +605,7 @@ public class EventTypes { entityEventDetails.put(EVENT_VM_MOVE, VirtualMachine.class); entityEventDetails.put(EVENT_VM_RESTORE, VirtualMachine.class); entityEventDetails.put(EVENT_VM_EXPUNGE, VirtualMachine.class); + entityEventDetails.put(EVENT_VM_DIAGNOSTICS, VirtualMachine.class); entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class); entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class); diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 504b2149837e..611b34e6bfc9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -121,6 +121,7 @@ public class ApiConstants { public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname"; public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode"; public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue"; + public static final String EXITCODE = "exitcode"; public static final String FENCE = "fence"; public static final String FETCH_LATEST = "fetchlatest"; public static final String FIRSTNAME = "firstname"; @@ -289,11 +290,14 @@ public class ApiConstants { public static final String START_PORT = "startport"; public static final String STATE = "state"; public static final String STATUS = "status"; + public static final String STDOUT = "stdout"; + public static final String STDERR = "stderr"; public static final String STORAGE_TYPE = "storagetype"; public static final String STORAGE_POLICY = "storagepolicy"; public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; public static final String STORAGE_CAPABILITIES = "storagecapabilities"; public static final String SYSTEM_VM_TYPE = "systemvmtype"; + public static final String TARGET_ID = "targetid"; public static final String TAGS = "tags"; public static final String TARGET_IQN = "targetiqn"; public static final String TEMPLATE_FILTER = "templatefilter"; @@ -719,6 +723,11 @@ public class ApiConstants { public static final String LAST_ANNOTATED = "lastannotated"; public static final String LDAP_DOMAIN = "ldapdomain"; + public static final String DETAIL = "detail"; + public static final String DISABLE_THRESHOLD = "disablethreshold"; + public static final String FILE_PATH = "filepath"; + public static final String FILE_AGE = "fileage"; + public enum HostDetails { all, capacity, events, stats, min; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RetrieveDiagnosticsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RetrieveDiagnosticsCmd.java new file mode 100644 index 000000000000..23b3a5382ea6 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/diagnostics/RetrieveDiagnosticsCmd.java @@ -0,0 +1,145 @@ +// 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.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +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.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.RetrieveDiagnosticsResponse; +import org.apache.cloudstack.api.response.SystemVmResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.diagnostics.RetrieveDiagnosticsService; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +@APICommand(name = RetrieveDiagnosticsCmd.APINAME, + description = "Retrieves diagnostics files from host VMs", + responseObject = RetrieveDiagnosticsResponse.class, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.12.0", + authorized = {RoleType.Admin}) +public class RetrieveDiagnosticsCmd extends BaseAsyncCmd { + + private static final Logger LOGGER = Logger.getLogger(RetrieveDiagnosticsCmd.class); + + public static final String APINAME = "retrieveDiagnostics"; + + @Inject + private RetrieveDiagnosticsService retrieveDiagnosticsService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.TARGET_ID, + type = CommandType.UUID, + entityType = SystemVmResponse.class, + validations = {ApiArgValidator.PositiveNumber}, + required = true, + description = "the Id of the system VM instance.") + private Long systemVmId; + + + @Parameter(name = ApiConstants.TYPE, + type = CommandType.STRING, + required = true, + description = "The type of diagnostics files requested, if DIAGNOSTICS_TYPE is not provided then the default files specified in the database will be retrieved") + private String type; + + @Parameter(name = ApiConstants.DETAIL, + type = CommandType.STRING, + description = "Optional comma separated list of diagnostics files or items, can be specified as filenames only or full file path. These come in addition to the defaults set in diagnosticstype") + private String optionalListOfFiles; + + @Parameter(name = ApiConstants.TIMEOUT, + type = CommandType.STRING, + description = "Time out setting in seconds for the overall API call.") + private String timeOut; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getOptionalListOfFiles() { + return optionalListOfFiles; + } + + public String getTimeOut() { + return timeOut; + } + + public Long getId() { + return systemVmId; + } + + @Override + public String getEventType() { + VirtualMachine.Type type = _mgr.findSystemVMTypeById(getId()); + return type.toString(); + } + public String getType() { + return type; + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventDescription() { + return "Retrieved diagnostics files from host =" + systemVmId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException { + CallContext.current().setEventDetails("Vm Id: " + this._uuidMgr.getUuid(VirtualMachine.class, getId())); + RetrieveDiagnosticsResponse response = new RetrieveDiagnosticsResponse(); + + try { + final String url = retrieveDiagnosticsService.getDiagnosticsFiles(this); + response.setUrl(url); + response.setObjectName("retrieved information"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + LOGGER.error("Failed to retrieve diagnostics files from ", ipve); + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ipve.getMessage()); + } catch (ConfigurationException cre) { + LOGGER.error("Failed to retrieve diagnostics files from ", cre); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, cre.getMessage()); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RetrieveDiagnosticsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RetrieveDiagnosticsResponse.java new file mode 100644 index 000000000000..96f1aba7281d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/RetrieveDiagnosticsResponse.java @@ -0,0 +1,41 @@ +// 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 RetrieveDiagnosticsResponse extends BaseResponse { + @SerializedName(ApiConstants.URL) + @Param(description = "Secondary storage URL were files have been downloaded to") + private String url; + + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/diagnostics/DeleteZipCommand.java b/api/src/main/java/org/apache/cloudstack/diagnostics/DeleteZipCommand.java new file mode 100644 index 000000000000..c41cd85d4fbe --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/diagnostics/DeleteZipCommand.java @@ -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.Command; +import com.cloud.agent.api.to.DataStoreTO; + +public class DeleteZipCommand extends Command { + private String zipFile; + private DataStoreTO destStore; + + public DeleteZipCommand(DataStoreTO destStore) { + this.zipFile = zipFile; + this.destStore = destStore; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public DataStoreTO getDestStore() { + return destStore; + } + + public String getDiagnosticsZipFile() { + return zipFile; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsService.java b/api/src/main/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsService.java new file mode 100644 index 000000000000..9d213b10a366 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsService.java @@ -0,0 +1,38 @@ +// 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.InvalidParameterValueException; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; +import org.apache.cloudstack.api.command.admin.diagnostics.RetrieveDiagnosticsCmd; +import org.apache.cloudstack.framework.config.impl.DiagnosticsKey; + +import javax.naming.ConfigurationException; +import java.util.List; +import java.util.Map; + + +public interface RetrieveDiagnosticsService extends Manager, PluggableService { + + String getDiagnosticsFiles(final RetrieveDiagnosticsCmd cmd) throws InvalidParameterValueException, ConfigurationException; + + boolean configure(final String name, final Map params) throws ConfigurationException; + + List get(String key); +} diff --git a/core/pom.xml b/core/pom.xml index 2313b29be758..1c986d534cb8 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -52,8 +52,13 @@ commons-compress 1.15 + + org.apache.cloudstack + cloud-engine-schema + 4.12.0.0-SNAPSHOT + compile + - diff --git a/core/src/main/java/com/cloud/agent/api/ExecuteScriptCommand.java b/core/src/main/java/com/cloud/agent/api/ExecuteScriptCommand.java new file mode 100644 index 000000000000..7b2a256fb8a0 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ExecuteScriptCommand.java @@ -0,0 +1,34 @@ +package com.cloud.agent.api; + +import com.cloud.agent.api.routing.NetworkElementCommand; +import org.apache.log4j.Logger; + +public class ExecuteScriptCommand extends NetworkElementCommand { + private static final Logger LOGGER = Logger.getLogger(ExecuteScriptCommand.class); + + private String commandScript; + private final boolean executeInSequence; + + public ExecuteScriptCommand(String commandScript, boolean executeInSequence) { + this.commandScript = commandScript; + this.executeInSequence = executeInSequence; + } + + + @Override + public boolean executeInSequence() + { + return this.executeInSequence; + } + + @Override + public boolean isQuery() { + + return true; + } + + public String getCommandScript() { + return commandScript; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/HandleDiagnosticsZipFileCommand.java b/core/src/main/java/com/cloud/agent/api/HandleDiagnosticsZipFileCommand.java new file mode 100644 index 000000000000..c3dd38d4418a --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/HandleDiagnosticsZipFileCommand.java @@ -0,0 +1,75 @@ +// +// 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.agent.api; + +import com.cloud.agent.api.to.DataStoreTO; + +public class HandleDiagnosticsZipFileCommand extends Command { + @LogLevel(LogLevel.Log4jLevel.Off) + private String diagnosticsFileData; + private String diagnosticsFile; + private boolean create = false; + private DataStoreTO destStore; + + public HandleDiagnosticsZipFileCommand(String diagnosticsFile, String diagnosticsFileData, DataStoreTO destStore, boolean create) { + this.diagnosticsFile = diagnosticsFile; + this.diagnosticsFileData = diagnosticsFileData; + this.destStore = destStore; + this.create = create; + } + + @Override + public boolean executeInSequence() { + return false; + } + + public boolean isCreate() { + return create; + } + + public void setCreate(boolean create) { + this.create = create; + } + + public DataStoreTO getDestStore() { + return destStore; + } + + public void setDestStore(DataStoreTO destStore) { + this.destStore = destStore; + } + + public String getDiagnosticsFileData() { + return diagnosticsFileData; + } + + public void setDiagnosticsFileData(String diagnosticsFileData) { + this.diagnosticsFileData = diagnosticsFileData; + } + + public String getDiagnosticsFile() { + return diagnosticsFile; + } + + public void setDiagnosticsFile(String diagnosticsFile) { + this.diagnosticsFile = diagnosticsFile; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/RetrieveDiagnosticsAnswer.java b/core/src/main/java/com/cloud/agent/api/RetrieveDiagnosticsAnswer.java new file mode 100644 index 000000000000..49779f32e03f --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RetrieveDiagnosticsAnswer.java @@ -0,0 +1,32 @@ +// 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.agent.api; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class RetrieveDiagnosticsAnswer extends Answer { + + public RetrieveDiagnosticsAnswer(NetworkElementCommand cmd, boolean result, String details) { + super(cmd, result, details); + } + + public String getOutput() { + return details; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RetrieveFilesCommand.java b/core/src/main/java/com/cloud/agent/api/RetrieveFilesCommand.java new file mode 100644 index 000000000000..5d3274e3ec71 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RetrieveFilesCommand.java @@ -0,0 +1,52 @@ +/* + * // 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.agent.api; + +import com.cloud.agent.api.routing.NetworkElementCommand; +import org.apache.log4j.Logger; + +public class RetrieveFilesCommand extends NetworkElementCommand { + private static final Logger LOGGER = Logger.getLogger(RetrieveFilesCommand.class); + + private String diagnosticFileToRetrieve; + private final boolean executeInSequence; + + public RetrieveFilesCommand(String fileToRetrieve, boolean executeInSequence) { + this.diagnosticFileToRetrieve = fileToRetrieve; + this.executeInSequence = executeInSequence; + } + + + @Override + public boolean executeInSequence() + { + return this.executeInSequence; + } + + @Override + public boolean isQuery() { + + return true; + } + + public String getDiagnosticFileToRetrieve() { + return diagnosticFileToRetrieve; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CopyRetrieveZipFilesAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/CopyRetrieveZipFilesAnswer.java new file mode 100644 index 000000000000..c77a2392d0f4 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CopyRetrieveZipFilesAnswer.java @@ -0,0 +1,46 @@ +// +// 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.agent.api.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class CopyRetrieveZipFilesAnswer extends Answer { + private String volumeFolder; + private String volumePath; + + protected CopyRetrieveZipFilesAnswer() { + super(); + } + + public CopyRetrieveZipFilesAnswer(Command command, boolean success, String details, String volumeFolder, String volumePath) { + super(command, success, details); + this.volumeFolder = volumeFolder; + this.volumePath = volumePath; + } + + public String getVolumeFolder() { + return volumeFolder; + } + + public String getVolumePath() { + return volumePath; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CopyRetrieveZipFilesCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CopyRetrieveZipFilesCommand.java new file mode 100644 index 000000000000..9f33eff490bb --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CopyRetrieveZipFilesCommand.java @@ -0,0 +1,91 @@ +// +// 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.agent.api.storage; + +import com.cloud.agent.api.to.DataTO; + +import java.util.Map; + +public class CopyRetrieveZipFilesCommand extends StorageNfsVersionCommand { + private String copyCommand; + private Boolean secCleanup = null; + private String content; + private String controlIp; + private final String tmpZipFilePath = "/tmp/diagnostics_*"; + private DataTO srcData; + private Map srcDetails; + private boolean executeInSequence; + + public CopyRetrieveZipFilesCommand(String copyCommand, String content, Boolean secCleanup, boolean executeInSequence) { + this.copyCommand = copyCommand; + this.secCleanup = secCleanup; + this.content = content; + this.executeInSequence = executeInSequence; + } + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + public String getCopyCommand() { + return copyCommand; + } + + public void setCopyCommand(String copyCommand) { + this.copyCommand = copyCommand; + } + + public Boolean getSecCleanup() { + return secCleanup; + } + + public void setSecCleanup(Boolean secCleanup) { + this.secCleanup = secCleanup; + } + + public void setSrcData(DataTO srcData) { + this.srcData = srcData; + } + + public DataTO getSrcData() { + return srcData; + } + + public void setSrcDetails(Map srcDetails) { + this.srcDetails = srcDetails; + } + + public String getControlIp() { + return controlIp; + } + + public void setControlIp(String controlIp) { + this.controlIp = controlIp; + } + + public String getTmpZipFilePath() { + return tmpZipFilePath; + } + + public String getContent() { + return content; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CopyVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CopyVolumeCommand.java index 863a9cad65bd..8ecd33f0d533 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/CopyVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/CopyVolumeCommand.java @@ -19,12 +19,12 @@ package com.cloud.agent.api.storage; -import java.util.Map; - import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.storage.StoragePool; +import java.util.Map; + public class CopyVolumeCommand extends StorageNfsVersionCommand { private long volumeId; private String volumePath; @@ -94,4 +94,6 @@ public void setSrcDetails(Map srcDetails) { public Map getSrcDetails() { return srcDetails; } + + } diff --git a/core/src/main/java/com/cloud/agent/api/storage/RetrieveFilesAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/RetrieveFilesAnswer.java new file mode 100644 index 000000000000..831202222ec3 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RetrieveFilesAnswer.java @@ -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 com.cloud.agent.api.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class RetrieveFilesAnswer extends Answer { + private String volumeFolder; + private String volumePath; + + private String secUrl; + + protected RetrieveFilesAnswer() { + super(); + } + + public RetrieveFilesAnswer(Command command, boolean success, String details, String volumeFolder, String volumePath, String secUrl) { + super(command, success, details); + this.volumeFolder = volumeFolder; + this.volumePath = volumePath; + this.secUrl = secUrl; + } + + public String getVolumeFolder() { + return volumeFolder; + } + + public String getVolumePath() { + return volumePath; + } + + public String getSecUrl() { + return secUrl; + } + +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java index 838f0877918f..99ea19e87292 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java @@ -58,6 +58,10 @@ public class VRScripts { public static final String MONITOR_SERVICE = "monitor_service.sh"; public static final String PASSWORD = "savepassword.sh"; public static final String ROUTER_ALERTS = "getRouterAlerts.sh"; + public static final String ROUTER_RETRIEVEFILES = "router.py"; + public static final String IPTABLES_RETRIEVEFILES = "iptables.py"; + public static final String IFCONFIG_RETRIEVEFILES = "ifconfig.py"; + public static final String ROUTE_RETRIEVEFILES = "route.py"; public static final String RVR_CHECK = "checkrouter.sh"; public static final String VMDATA = "vmdata.py"; public static final String RVR_BUMPUP_PRI = "bumpup_priority.sh"; @@ -69,4 +73,7 @@ public class VRScripts { public static final String VR_CFG = "vr_cfg.sh"; + // Script for Retrieve Diagnostics + public static final String RETRIEVE_DIAGNOSTICS = "retrieve_diagnostics.py"; + } \ No newline at end of file diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 0ffe8cc0ea2f..3d6ee836fbe5 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -19,37 +19,17 @@ 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; import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; +import com.cloud.agent.api.ExecuteScriptCommand; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.GetRouterAlertsAnswer; +import com.cloud.agent.api.RetrieveDiagnosticsAnswer; +import com.cloud.agent.api.RetrieveFilesCommand; import com.cloud.agent.api.routing.AggregationControlCommand; import com.cloud.agent.api.routing.AggregationControlCommand.Action; import com.cloud.agent.api.routing.GetRouterAlertsCommand; @@ -59,6 +39,27 @@ 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.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 @@ -125,6 +126,10 @@ public Answer executeRequest(final NetworkElementCommand cmd) { return execute((AggregationControlCommand)cmd); } + if (cmd instanceof RetrieveFilesCommand) { + return execute((RetrieveFilesCommand)cmd); + } + if (_vrAggregateCommandsSet.containsKey(routerName)) { _vrAggregateCommandsSet.get(routerName).add(cmd); aggregated = true; @@ -152,6 +157,24 @@ public Answer executeRequest(final NetworkElementCommand cmd) { } } + private RetrieveDiagnosticsAnswer execute(final RetrieveFilesCommand cmd) { + String args = cmd.getDiagnosticFileToRetrieve(); + ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), args, null); + if (result.isSuccess()) { + return new RetrieveDiagnosticsAnswer(cmd, true, result.getDetails()); + } + return new RetrieveDiagnosticsAnswer(cmd, result.isSuccess(), result.getDetails()); + } + + private RetrieveDiagnosticsAnswer execute(final ExecuteScriptCommand cmd) { + String args = cmd.getCommandScript(); + ExecutionResult result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), args, null); + if (result.isSuccess()) { + return new RetrieveDiagnosticsAnswer(cmd, false, result.getDetails()); + } + return new RetrieveDiagnosticsAnswer(cmd, result.isSuccess(), result.getDetails()); + } + private Answer execute(final SetupKeyStoreCommand cmd) { final String args = String.format("/usr/local/cloud/systemvm/conf/agent.properties " + "/usr/local/cloud/systemvm/conf/%s " + @@ -192,6 +215,10 @@ private Answer executeQueryCommand(NetworkElementCommand cmd) { return execute((CheckS2SVpnConnectionsCommand) cmd); } else if (cmd instanceof GetRouterAlertsCommand) { return execute((GetRouterAlertsCommand)cmd); + } else if (cmd instanceof RetrieveFilesCommand) { + return execute((RetrieveFilesCommand) cmd); + } else if (cmd instanceof ExecuteScriptCommand) { + return execute((ExecuteScriptCommand) cmd); } else { s_logger.error("Unknown query command in VirtualRoutingResource!"); return Answer.createUnsupportedCommandAnswer(cmd); diff --git a/core/src/main/java/org/apache/cloudstack/diagnostics/RetrieveZipFilesCommand.java b/core/src/main/java/org/apache/cloudstack/diagnostics/RetrieveZipFilesCommand.java new file mode 100644 index 000000000000..017c673ffc4b --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/diagnostics/RetrieveZipFilesCommand.java @@ -0,0 +1,49 @@ +// +// 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.storage.StorageNfsVersionCommand; + +public class RetrieveZipFilesCommand extends StorageNfsVersionCommand { + private Boolean secCleanup = null; + private boolean executeInSequence; + + public RetrieveZipFilesCommand(Boolean secCleanup, boolean executeInSequence) { + this.executeInSequence = executeInSequence; + this.secCleanup = secCleanup; + } + + public RetrieveZipFilesCommand() { + } + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + public Boolean getSecCleanup() { + return secCleanup; + } + + public void setSecCleanup(Boolean secCleanup) { + this.secCleanup = secCleanup; + } + +} diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql b/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql index d5e6d61ea71d..525b0069269a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41110to41200.sql @@ -32,4 +32,24 @@ ALTER TABLE `vlan` CHANGE `description` `ip4_range` varchar(255); -- We are only adding the permission to the default rules. Any custom rule must be configured by the root admin. INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 2, 'moveNetworkAclItem', 'ALLOW', 100) ON DUPLICATE KEY UPDATE rule=rule; INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 3, 'moveNetworkAclItem', 'ALLOW', 302) ON DUPLICATE KEY UPDATE rule=rule; -INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'moveNetworkAclItem', 'ALLOW', 260) ON DUPLICATE KEY UPDATE rule=rule; \ No newline at end of file +INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 4, 'moveNetworkAclItem', 'ALLOW', 260) ON DUPLICATE KEY UPDATE rule=rule; + +CREATE TABLE `cloud`.`diagnosticsdata` ( + `role` varchar(40) NOT NULL COMMENT 'role as for system vm', + `class` varchar(30) NOT NULL COMMENT 'the kind of diagnostics files', + `value` varchar(200) NOT NULL COMMENT 'default comma delimited list of files' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('SecondaryStorageVm', 'LOGFILES', '/var/log/cloud.log,/var/log/auth.log,/var/log/daemon.log,[IPTABLES],[ROUTE]'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('SecondaryStorageVm', 'PROPERTYFILES', ''); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('DomainRouter', 'DHCPFILES', 'dnsmasq.conf,resolv.conf,cloud.log,[IPTABLES],[IFCONFIG]'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('DomainRouter', 'USERDATA', ''); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('DomainRouter', 'LB', 'haproxy.conf'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('DomainRouter', 'DNS', '/var/log/cloud.log,/var/log/auth.log,/var/log/daemon.log,[ROUTE],[IFCONFIG],[IPTABLES]'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('DomainRouter', 'VPN', ''); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('DomainRouter', 'LOGFILES', '/var/log/cloud.log,/var/log/auth.log,/var/log/daemon.log,[IPTABLES],[ROUTE]'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('ConsoleProxy', 'PROPERTYFILES', ''); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('ALL', 'IPTABLES.retrieve', 'iptables.sh'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('ALL', 'IFCONFIG.retrieve', 'ifconfig.sh'); +INSERT INTO `cloud`.`diagnosticsdata` (`role`, `class`, `value`) values ('ALL', 'ROUTE.retrieve', 'route.sh'); + diff --git a/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/org/apache/cloudstack/storage/diagnostics/Diagnostics.java b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/org/apache/cloudstack/storage/diagnostics/Diagnostics.java new file mode 100644 index 000000000000..e031a2658171 --- /dev/null +++ b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/org/apache/cloudstack/storage/diagnostics/Diagnostics.java @@ -0,0 +1,15 @@ +package org.apache.cloudstack.storage.configdrive.org.apache.cloudstack.storage.diagnostics; + +public class Diagnostics { + public final static String DIAGNOSTICSDIR = "diagnosticsdata"; + + public static final String cloudStackConfigDriveName = "/cloudstack/"; + public static String createDiagnosticsPath(String instanceName) { + return Diagnostics.DIAGNOSTICSDIR + "/" + diagnosticsFileName(instanceName); + } + + public static String diagnosticsFileName(String instanceName) { + return instanceName + ".zip"; + } + +} diff --git a/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/org/apache/cloudstack/storage/diagnostics/DiagnosticsBuilder.java b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/org/apache/cloudstack/storage/diagnostics/DiagnosticsBuilder.java new file mode 100644 index 000000000000..3cfb6a4a895f --- /dev/null +++ b/engine/storage/configdrive/src/main/java/org/apache/cloudstack/storage/configdrive/org/apache/cloudstack/storage/diagnostics/DiagnosticsBuilder.java @@ -0,0 +1,88 @@ +package org.apache.cloudstack.storage.configdrive.org.apache.cloudstack.storage.diagnostics; + +import com.cloud.utils.StringUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; +import org.joda.time.Duration; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class DiagnosticsBuilder { + public static final Logger LOG = Logger.getLogger(DiagnosticsBuilder.class); + + static void writeFile(File folder, String file, String content) { + try { + FileUtils.write(new File(folder, file), content, com.cloud.utils.StringUtils.getPreferredCharset(), false); + } catch (IOException ex) { + throw new CloudRuntimeException("Failed to create diagnostics drive file " + file, ex); + } + } + + public static String buildConfigDrive(String folder, String diagnosticsFileName) { + if (diagnosticsFileName == null) { + throw new CloudRuntimeException("No zip file provided"); + } + byte[] decoded = Base64.decodeBase64(diagnosticsFileName.getBytes(StandardCharsets.US_ASCII)); + Path destPath = Paths.get(folder, diagnosticsFileName); + File typeFolder = new File(Diagnostics.DIAGNOSTICSDIR); + if (!typeFolder.exists() && !typeFolder.mkdirs()) { + throw new CloudRuntimeException("Failed to create folder: " + typeFolder); + } + try { + Path tempDir = Files.createTempDirectory(Diagnostics.DIAGNOSTICSDIR); + Files.createDirectories(destPath.getParent()); + File zipFile = Files.write(destPath, decoded).toFile(); + linkUserData(destPath.toString()); + return zipFile.toString(); + + } catch (IOException e) { + throw new CloudRuntimeException("Failed due to", e); + } + } + + public static File base64StringToFile(String encodedIsoData, String folder, String fileName) throws IOException { + byte[] decoded = Base64.decodeBase64(encodedIsoData.getBytes(StandardCharsets.US_ASCII)); + Path destPath = Paths.get(folder, fileName); + try { + Files.createDirectories(destPath.getParent()); + } catch (final IOException e) { + LOG.warn("Exception hit while trying to recreate directory: " + destPath.getParent().toString()); + } + return Files.write(destPath, decoded).toFile(); + } + + private static void deleteTempDir(Path tempDir) { + try { + if (tempDir != null) { + FileUtils.deleteDirectory(tempDir.toFile()); + } + } catch (IOException ioe) { + LOG.warn("Failed to delete Diagnostics temporary directory: " + tempDir.toString(), ioe); + } + } + + static void linkUserData(String tempDirName) { + String userDataFilePath = tempDirName + Diagnostics.cloudStackConfigDriveName + "userdata/user_data.txt"; + File file = new File(userDataFilePath); + if (file.exists()) { + Script hardLink = new Script("ln", Duration.standardSeconds(300), LOG); + hardLink.add(userDataFilePath); + hardLink.add(tempDirName + Diagnostics.DIAGNOSTICSDIR); + LOG.debug("execute command: " + hardLink.toString()); + + String executionResult = hardLink.execute(); + if (StringUtils.isNotBlank(executionResult)) { + throw new CloudRuntimeException("Unable to create user_data link due to " + executionResult); + } + } + } + +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/DiagnosticsConfigurator.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/DiagnosticsConfigurator.java new file mode 100644 index 000000000000..675250875fbf --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/DiagnosticsConfigurator.java @@ -0,0 +1,39 @@ +/* + * // 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.framework.config; + +import org.apache.cloudstack.framework.config.impl.DiagnosticsKey; + +import java.util.HashMap; +import java.util.List; + +public interface DiagnosticsConfigurator { + + void set(DiagnosticsKey key, String value); + + void createOrUpdateDiagnosticObject(DiagnosticsKey key); + + void populateDiagnostics(DiagnosticsKey configurable); + + void setDiagnosticsKeyHashMap(HashMap> diagnosticsKeyHashMap); + + HashMap> getDiagnosticsTypeLevelsMap(); +} + diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/RetrieveDiagnosticsDaoImpl.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/RetrieveDiagnosticsDaoImpl.java new file mode 100644 index 000000000000..1882cc2b9471 --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/RetrieveDiagnosticsDaoImpl.java @@ -0,0 +1,86 @@ +/* + * // 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.framework.config; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import org.apache.cloudstack.framework.config.impl.RetrieveDiagnosticsDao; +import org.apache.cloudstack.framework.config.impl.RetrieveDiagnosticsVO; +import org.springframework.stereotype.Component; +import org.apache.log4j.Logger; + +import java.sql.PreparedStatement; +import java.util.List; + +@Component +public class RetrieveDiagnosticsDaoImpl extends GenericDaoBase implements RetrieveDiagnosticsDao +{ + private static final Logger LOGGER = Logger.getLogger(RetrieveDiagnosticsDaoImpl.class); + private final SearchBuilder DiagnosticsSearchByTypeAndRole; + public static final String UPDATE_RETRIEVEDIAGNOSTICS_SQL = "INSERT INTO diagnosticsdata VALUES (?, ?, ?)"; + + public RetrieveDiagnosticsDaoImpl() { + super(); + DiagnosticsSearchByTypeAndRole = createSearchBuilder(); + DiagnosticsSearchByTypeAndRole.and("class", DiagnosticsSearchByTypeAndRole.entity().getType(), SearchCriteria.Op.EQ); + DiagnosticsSearchByTypeAndRole.and("role", DiagnosticsSearchByTypeAndRole.entity().getRole(), SearchCriteria.Op.EQ); + DiagnosticsSearchByTypeAndRole.done(); + } + + @Override public List findByEntityType(String diagnosticsType) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("class", SearchCriteria.Op.EQ, diagnosticsType); + return listBy(sc); + } + + @Override public List findByEntity(String diagnosticsType, String role) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("class", SearchCriteria.Op.EQ, diagnosticsType); + sc.addAnd("role", SearchCriteria.Op.EQ, role); + return listBy(sc, null); + } + + @Override + public List retrieveAllDiagnosticsData() { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("class", SearchCriteria.Op.IN, "ConsoleProxy, SecondaryStorageVm, VirtualRouter"); + return listBy(sc, null); + } + + @Override + public boolean update(String role, String clazz, String value) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + try (PreparedStatement stmt = txn.prepareStatement(UPDATE_RETRIEVEDIAGNOSTICS_SQL);){ + stmt.setString(1, role); + stmt.setString(2, clazz); + stmt.setString(3, value); + stmt.executeUpdate(); + return true; + } catch (Exception e) { + LOGGER.warn("Unable to update Diagnosticsdata Table", e); + } + return false; + } + +} + + diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/DiagnosticsConfiguratorImpl.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/DiagnosticsConfiguratorImpl.java new file mode 100644 index 000000000000..8a9a52350ef2 --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/DiagnosticsConfiguratorImpl.java @@ -0,0 +1,103 @@ +// 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.framework.config.impl; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.framework.config.DiagnosticsConfigurator; +import org.apache.commons.lang.ObjectUtils; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.HashMap; +import java.util.List; + +public class DiagnosticsConfiguratorImpl implements DiagnosticsConfigurator { + + private final static Logger LOGGER = Logger.getLogger(DiagnosticsConfiguratorImpl.class); + @Inject + RetrieveDiagnosticsDao _diagnosticsDao; + + + HashMap> diagnosticsKeyHashMap = null; + + public DiagnosticsConfiguratorImpl() { + } + + @Override + public void populateDiagnostics(DiagnosticsKey clazz) { + List previous = diagnosticsKeyHashMap.get(clazz.key()); + for (DiagnosticsKey key : previous) { + if (key == null && !key.getDiagnosticsClassType().equals(clazz.getDiagnosticsClassType())) { + throw new CloudRuntimeException("Diagnostics type " + clazz.getDiagnosticsClassType() + " is not one of the diagnostic types"); + } + if (key.getRole().equalsIgnoreCase(clazz.getRole()) && !key.getDetail().equalsIgnoreCase(clazz.getDetail())) { + key.setDetail(clazz.getDetail()); + } else { + DiagnosticsKey newDiagnosticsType = new DiagnosticsKey(clazz.getRole(), clazz.getDiagnosticsClassType(), clazz.getDetail(), clazz.description()); + createOrUpdateDiagnosticObject(newDiagnosticsType ); + } + } + } + + @Override + public void setDiagnosticsKeyHashMap(HashMap> diagnosticsKeyHashMap) { + this.diagnosticsKeyHashMap = diagnosticsKeyHashMap; + } + + + @Override + public void createOrUpdateDiagnosticObject(DiagnosticsKey diagnosticsType) { + List voList = _diagnosticsDao.findByEntity(diagnosticsType.getDiagnosticsClassType(), diagnosticsType.getRole()); + for (RetrieveDiagnosticsVO vo : voList) { + if (vo == null) { + vo = new RetrieveDiagnosticsVO(diagnosticsType.getRole(), diagnosticsType.getDiagnosticsClassType(), diagnosticsType.getDetail()); + vo.setType(diagnosticsType.getRole()); + vo.setRole(diagnosticsType.getDiagnosticsClassType());//to be given SystemVM type + vo.setDefaultValue(diagnosticsType.getDetail());//to be populated + _diagnosticsDao.persist(vo); + + } else { + if (vo.getDefaultValue() != diagnosticsType.getDiagnosticsClassType() || !ObjectUtils.equals(vo.getRole(), diagnosticsType.getRole()) || !ObjectUtils.equals(vo.getDefaultValue(), + diagnosticsType.getDetail())) { + vo.setRole(diagnosticsType.value()); //to be changed + vo.setType(diagnosticsType.key()); + vo.setDefaultValue(diagnosticsType.getDetail()); //to be changed + _diagnosticsDao.persist(vo); + } + } + } + } + + @Override + public HashMap> getDiagnosticsTypeLevelsMap() { + return diagnosticsKeyHashMap; + } + + public void setDiagnosticsTypeLevelsMap(HashMap> diagnosticsTypeLevelsMap) { + this.diagnosticsKeyHashMap = diagnosticsTypeLevelsMap; + } + + public RetrieveDiagnosticsDao global() { + return _diagnosticsDao; + } + + @Override + public void set(DiagnosticsKey key, String value) { + + } + +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/DiagnosticsKey.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/DiagnosticsKey.java new file mode 100644 index 000000000000..f0ce3b0b170f --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/DiagnosticsKey.java @@ -0,0 +1,161 @@ +// 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.framework.config.impl; + +import com.cloud.utils.exception.CloudRuntimeException; + +import java.util.List; + +public class DiagnosticsKey { + public static enum DiagnosticsEntryType { + IPTABLES, LOGFILES, PROPERTYFILES, DHCPFILES, USERDATA, LB, DNS, VPN, IPTABLESretrieve, IPTABLESremove; + @Override + public String toString() { + return "IPTABLES, LOGFILES, PROPERTYFILES, DHCPFILES, USERDATA, LB, DNS, VPN, IPTABLESretrieve, IPTABLESremove"; + } + } + + private String role; + private String diagnosticsClassType; + private String description; + private String detail; + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public String getDiagnosticsClassType() { + return diagnosticsClassType; + } + + public void setDiagnosticsClassType(String diagnosticsClassType) { + this.diagnosticsClassType = diagnosticsClassType; + } + + public final String key() { + return diagnosticsClassType; + } + + + public String description() { + return description; + } + + + + @Override + public String toString() + { + return diagnosticsClassType; + } + + static DiagnosticsConfiguratorImpl s_depot = new DiagnosticsConfiguratorImpl(); + + + public DiagnosticsKey() { + + } + + public DiagnosticsKey(String role, String diagnosticsType, String detail, String description) { + this.diagnosticsClassType = diagnosticsType; + this.description = description; + this.detail = detail; + this.role = role; + } + + @Override + public int hashCode() + { + return diagnosticsClassType.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DiagnosticsKey) { + DiagnosticsKey that = (DiagnosticsKey)obj; + return this.diagnosticsClassType.equals(that.diagnosticsClassType); + } + return false; + } + + public boolean isSameKeyAs(Object obj) { + if(this.equals(obj)) { + return true; + } else if (obj instanceof String) { + String key = (String)obj; + return key.equals(diagnosticsClassType); + } + + throw new CloudRuntimeException("Comparing Diagnostics key to " + obj.toString()); + } + + public String value() { + if (diagnosticsClassType == null) { + RetrieveDiagnosticsVO vo = s_depot != null ? s_depot.global().findById(key()) : null; + final String value = (vo != null && vo.getDefaultValue() != null) ? vo.getDefaultValue() : null; + diagnosticsClassType = (String)((value == null) ? null : valueOf(value)); + } + + return diagnosticsClassType; + } + + public String valueIn(String id) { + if (id == null) { + return value(); + } + + List valueVO = s_depot != null ? s_depot.global().findByEntityType((key())) : null; + for (RetrieveDiagnosticsVO vo : valueVO) { + String value = vo.getType().toString(); + if (value == null) { + return value(); + } else { + return valueOf(value); + } + } + return null; + } + + @SuppressWarnings("unchecked") + protected String valueOf(String value) { + Class type = value.getClass(); + if (!type.isAssignableFrom(String.class)) { + throw new CloudRuntimeException("Unsupported data type for config values: " + type); + } else { + return String.valueOf(value); + } + } + + public DiagnosticsEntryType[] getDiagnosticsTypeKeys() { + return DiagnosticsEntryType.values(); + } + +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnostics.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnostics.java new file mode 100644 index 000000000000..6d68ea4ed352 --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnostics.java @@ -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.framework.config.impl; + +public interface RetrieveDiagnostics { + String getDefaultValue(); + + String getRole(); + + String getType(); + +} diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnosticsDao.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnosticsDao.java new file mode 100644 index 000000000000..2460792e8d5a --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnosticsDao.java @@ -0,0 +1,32 @@ +// 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.framework.config.impl; + +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface RetrieveDiagnosticsDao extends GenericDao { + + List findByEntityType(String entityType); + List findByEntity(String entityType, String entityUuid); + List retrieveAllDiagnosticsData(); + boolean update(String role, String clazz, String value); +} + + diff --git a/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnosticsVO.java b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnosticsVO.java new file mode 100644 index 000000000000..ef092733ff1e --- /dev/null +++ b/framework/config/src/main/java/org/apache/cloudstack/framework/config/impl/RetrieveDiagnosticsVO.java @@ -0,0 +1,83 @@ +// 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.framework.config.impl; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name = "diagnosticsdata") +public class RetrieveDiagnosticsVO implements RetrieveDiagnostics { + + @Column(name = "role") + private String role; + + @Column(name = "class") + private String type; + + @Column(name = "value", length = 8191) + private String value; + + protected RetrieveDiagnosticsVO() { + } + + public RetrieveDiagnosticsVO(String role, String className, String value) { + setRole(role); + setType(className); + setDefaultValue(value); + } + + public RetrieveDiagnosticsVO(String role, DiagnosticsKey.DiagnosticsEntryType type, String value) { + setRole(role); + setType(type.toString()); + setDefaultValue(value); + } + + public RetrieveDiagnosticsVO(String name, String value) { + this.role = name; + setDefaultValue(value); + } + + @Override + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + @Override + public String getDefaultValue() { + return value; + } + + public void setDefaultValue(String value) { + this.value = value; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + +} diff --git a/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml b/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml index be116fc5e775..e3b3c3a1706b 100644 --- a/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml +++ b/framework/config/src/main/resources/META-INF/cloudstack/system/spring-framework-config-system-context.xml @@ -48,4 +48,10 @@ + + + + diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 755dbaf68f7d..b49469a176f8 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -16,68 +16,6 @@ // under the License. package com.cloud.hypervisor.kvm.resource; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.net.InetAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.naming.ConfigurationException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import com.cloud.resource.RequestWrapper; -import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; -import org.apache.cloudstack.storage.to.VolumeObjectTO; -import org.apache.cloudstack.utils.hypervisor.HypervisorUtils; -import org.apache.cloudstack.utils.linux.CPUStat; -import org.apache.cloudstack.utils.linux.MemStat; -import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; -import org.apache.cloudstack.utils.security.KeyStoreUtils; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.ArrayUtils; -import org.apache.commons.lang.math.NumberUtils; -import org.apache.log4j.Logger; -import org.joda.time.Duration; -import org.libvirt.Connect; -import org.libvirt.Domain; -import org.libvirt.DomainBlockStats; -import org.libvirt.DomainInfo; -import org.libvirt.DomainInfo.DomainState; -import org.libvirt.DomainInterfaceStats; -import org.libvirt.DomainSnapshot; -import org.libvirt.LibvirtException; -import org.libvirt.MemoryStatistic; -import org.libvirt.NodeInfo; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.HostVmStateReportEntry; @@ -95,6 +33,7 @@ import com.cloud.agent.api.routing.IpAssocVpcCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.SetSourceNatCommand; +import com.cloud.agent.api.storage.CopyRetrieveZipFilesCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; @@ -145,6 +84,7 @@ import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.RouterPrivateIpStrategy; import com.cloud.network.Networks.TrafficType; +import com.cloud.resource.RequestWrapper; import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.JavaStorageLayer; @@ -170,6 +110,67 @@ import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VmDetailConstants; import com.google.common.base.Strings; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.utils.hypervisor.HypervisorUtils; +import org.apache.cloudstack.utils.linux.CPUStat; +import org.apache.cloudstack.utils.linux.MemStat; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.security.KeyStoreUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.math.NumberUtils; +import org.apache.log4j.Logger; +import org.joda.time.Duration; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.DomainBlockStats; +import org.libvirt.DomainInfo; +import org.libvirt.DomainInfo.DomainState; +import org.libvirt.DomainInterfaceStats; +import org.libvirt.DomainSnapshot; +import org.libvirt.LibvirtException; +import org.libvirt.MemoryStatistic; +import org.libvirt.NodeInfo; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.net.InetAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * LibvirtComputingResource execute requests on the computing/routing host using @@ -223,6 +224,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public static final String SSHKEYSPATH = "/root/.ssh"; public static final String SSHPRVKEYPATH = SSHKEYSPATH + File.separator + "id_rsa.cloud"; public static final String SSHPUBKEYPATH = SSHKEYSPATH + File.separator + "id_rsa.pub.cloud"; + protected static final int DEFAULT_DOMR_SSHPORT = 3922; public static final String BASH_SCRIPT_PATH = "/bin/bash"; @@ -482,6 +484,78 @@ public StorageSubsystemCommandHandler getStorageHandler() { return storageHandler; } + /* public Answer copyFilesFromHost(RetrieveZipFilesCommand command) { + Script script = new Script(command.getCopyCommand()); + String result = script.execute(); + if (result != null) { + return new Answer(command, true, result); + } + return null; + }*/ + + public File getSystemVMKeyFile() { + final URL url = this.getClass().getClassLoader().getResource("scripts/vm/systemvm/id_rsa.cloud"); + File keyFile = null; + if (url != null) { + keyFile = new File(url.getPath()); + } + if (keyFile == null || !keyFile.exists()) { + keyFile = new File("/usr/share/cloudstack-common/scripts/vm/systemvm/id_rsa.cloud"); + } + assert keyFile != null; + if (!keyFile.exists()) { + s_logger.error("Unable to locate id_rsa.cloud in your setup at " + keyFile.toString()); + } + return keyFile; + } + + public Answer copyZipFileToHost(CopyRetrieveZipFilesCommand command) {//String controlIp, String filename, final String filePath, String content) { + final File keyFile = getSystemVMKeyFile(); + boolean successful = false; + final File dir = new File("/opt/cloud/bin/diagnosticsdata"); + if (!dir.exists()) { + successful = dir.mkdir(); + } + if (!successful) { + throw new CloudRuntimeException("Failed to create directory /opt/cloud/bin/diagnosticsdata"); + } + try { + SshHelper.scpTo(command.getControlIp(), DEFAULT_DOMR_SSHPORT, "root", keyFile, null, dir.toString(), command.getContent().getBytes(Charset.forName("UTF-8")), command.getTmpZipFilePath(), null); + } catch (final Exception e) { + s_logger.warn("Fail to create file " + command.getTmpZipFilePath() + " in System VM " + command.getControlIp(), e); + return new Answer(command, e); + } + return new Answer(command, null); + } + + public Answer copyZipFileToServer(CopyRetrieveZipFilesCommand cmd) { + final File keyFile = getSystemVMKeyFile(); + boolean successful = false; + final File dir = new File("/tmp/"); + if (!dir.exists()) { + successful = dir.mkdir(); + } + if (!successful) { + throw new CloudRuntimeException("Failed to create directory /tmp/"); + } + try { + SshHelper.scpTo(cmd.getControlIp(), 8250, "root", keyFile, null, dir.toString(), cmd.getContent().getBytes(Charset.forName("UTF-8")), cmd.getTmpZipFilePath(), null); + } catch (final Exception e) { + s_logger.warn("Fail to create file " + cmd.getTmpZipFilePath() + " in System VM " + cmd.getControlIp(), e); + return new Answer(cmd, e); + } + return new Answer(cmd, null); + } + + public Answer deleteFilesFromHost(CopyRetrieveZipFilesCommand command) { + Script script = new Script(command.getCopyCommand()); + String result = script.execute(); + if (result != null) { + return new Answer(command, true, result); + } + return null; + } + private static final class KeyValueInterpreter extends OutputInterpreter { private final Map map = new HashMap(); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRetrieveZipFileWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRetrieveZipFileWrapper.java new file mode 100644 index 000000000000..79b8594fc6bb --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRetrieveZipFileWrapper.java @@ -0,0 +1,36 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +package com.cloud.hypervisor.kvm.resource.wrapper; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.storage.CopyRetrieveZipFilesCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import org.apache.log4j.Logger; + +@ResourceWrapper(handles = CopyRetrieveZipFilesCommand.class) +public class LibvirtRetrieveZipFileWrapper extends CommandWrapper { + private static final Logger LOGGER = Logger.getLogger(LibvirtRetrieveZipFileWrapper.class); + + @Override + public Answer execute(final CopyRetrieveZipFilesCommand command, final LibvirtComputingResource libvirtComputingResource) { + return null; + } +} diff --git a/server/src/main/java/org/apache/cloudstack/diagnostics/CompressedFileUploader.java b/server/src/main/java/org/apache/cloudstack/diagnostics/CompressedFileUploader.java new file mode 100644 index 000000000000..88c8bd042d0b --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/diagnostics/CompressedFileUploader.java @@ -0,0 +1,28 @@ +package org.apache.cloudstack.diagnostics; + +public interface CompressedFileUploader extends Runnable { + + public interface UploadCompleteCallback { + void uploadComplete(Status status); + + } + + public static enum Status { + UNKNOWN, NOT_STARTED, IN_PROGRESS, ABORTED, UNRECOVERABLE_ERROR, RECOVERABLE_ERROR, UPLOAD_FINISHED, POST_UPLOAD_FINISHED + } + + public String upload(UploadCompleteCallback callback); + + public CompressedFileUploader.Status getStatus(); + + public String getUploadError(); + + public String getUploadLocalPath(); + + public void setStatus(CompressedFileUploader.Status status); + + public void setUploadError(String string); + + public void setResume(boolean resume); + +} diff --git a/server/src/main/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsServiceImpl.java b/server/src/main/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsServiceImpl.java new file mode 100644 index 000000000000..7a53b8963367 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsServiceImpl.java @@ -0,0 +1,1139 @@ +// 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.ExecuteScriptCommand; +import com.cloud.agent.api.HandleDiagnosticsZipFileCommand; +import com.cloud.agent.api.RetrieveDiagnosticsAnswer; +import com.cloud.agent.api.RetrieveFilesCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.storage.CopyRetrieveZipFilesCommand; +import com.cloud.agent.api.storage.CreateEntityDownloadURLAnswer; +import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand; +import com.cloud.agent.api.storage.DeleteEntityDownloadURLCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.capacity.Capacity; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.capacity.dao.CapacityDaoImpl; +import com.cloud.cluster.dao.ManagementServerHostDao; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.NetworkModel; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.element.ConfigDriveNetworkElement; +import com.cloud.network.guru.NetworkGuru; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceState; +import com.cloud.server.ManagementServer; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.Storage; +import com.cloud.storage.StorageLayer; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Upload; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeDetailVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.user.AccountManager; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.vm.DiskProfile; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfileImpl; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.SecondaryStorageVmDao; +import com.cloud.vm.dao.VMInstanceDao; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.admin.diagnostics.RetrieveDiagnosticsCmd; +import org.apache.cloudstack.config.Configuration; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +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.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.framework.config.ConfigDepot; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.DiagnosticsConfigurator; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.framework.config.impl.DiagnosticsKey; +import org.apache.cloudstack.framework.config.impl.RetrieveDiagnosticsDao; +import org.apache.cloudstack.framework.config.impl.RetrieveDiagnosticsVO; +import org.apache.cloudstack.storage.configdrive.org.apache.cloudstack.storage.diagnostics.Diagnostics; +import org.apache.cloudstack.storage.configdrive.org.apache.cloudstack.storage.diagnostics.DiagnosticsBuilder; +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.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class RetrieveDiagnosticsServiceImpl extends ManagerBase implements RetrieveDiagnosticsService, Configurable { + + private static final Logger LOGGER = Logger.getLogger(RetrieveDiagnosticsServiceImpl.class); + + protected StorageLayer _storage; + private Long _timeOut; + + protected Map configParams = new HashMap(); + private Map _configs; + + private String scriptName = null; + private String diagnosticsType = null; + private String dir; + + private Long hostId = null; + private VMInstanceVO vmInstance; + private String uuid; + private final Map jobs = new ConcurrentHashMap(); + + ScheduledExecutorService _executor = null; + + HashMap> allDefaultDiagnosticsTypeKeys = new HashMap>(); + + private Map accessDetails; + + private Boolean gcEnabled; + private Long interval; + + private Float disableThreshold; + private String filePath; + private Long fileAge; + private String diagnosticsZipFileName; + private String secondaryUrl; + private String parentDir; + + private ExecutorService threadPool; + + private Integer nfsVersion; + private static final Random RANDOM = new Random(System.nanoTime()); + + protected String _parent = "/mnt/SecStorage"; + + protected boolean inSystemVm = false; + List networkGurus; + private String secUrl; + + @Inject + VolumeDataFactory volumeFactory; + + @Inject + private VolumeDetailsDao volumeDetailsDao; + + @Inject + NetworkModel _networkModel; + + @Inject + NetworkDao _networksDao = null; + + @Inject + NicDao _nicDao = null; + + @Inject + private HostDao _hostDao; + + @Inject + private SecondaryStorageVmDao _secStorageVmDao; + + @Inject + private DataStoreManager _dataStoreMgr; + + @Inject + private AgentManager _agentMgr; + + @Inject + private ManagementServerHostDao managementServerHostDao; + + @Inject + public AccountManager _accountMgr; + + @Inject + protected ConfigurationDao _configDao; + + @Inject + protected ResourceManager _resourceMgr; + + @Inject + private NetworkOrchestrationService networkManager; + + @Inject + protected ManagementServer _serverMgr; + + @Inject + EndPointSelector _ep; + + + @Inject + private RetrieveDiagnosticsDao _retrieveDiagnosticsDao; + + @Inject + private ConfigDepot _configDepot; + + @Inject + private DiagnosticsConfigurator _diagnosticsDepot; + + @Inject + private VolumeService _volumeService; + + @Inject + private ConfigDepot configDepot; + + @Inject + private HostDetailsDao _hostDetailDao; + + @Inject + protected VMInstanceDao _vmDao; + + @Inject + private ServiceOfferingDao _offeringDao; + + @Inject + private VolumeDao _volumeDao; + + @Inject private ClusterDao clusterDao; + + @Inject + private DiskOfferingDao _diskOfferingDao; + + @Inject + private PrimaryDataStoreDao _poolDao; + + @Inject + private CapacityDao _capacityDao; + + @Inject + private VirtualMachineManager vmManager; + + @Inject + protected DataCenterDao _dcDao = null; + + @Inject + protected PrimaryDataStoreDao _storagePoolDao = null; + + ConfigKey RetrieveDiagnosticsTimeOut = new ConfigKey("Advanced", Long.class, "retrieveDiagnostics.retrieval.timeout", "3600", + "The timeout setting in seconds for the overall API call", true, ConfigKey.Scope.Global); + ConfigKey RetrieveDiagnosticsGCEnable = new ConfigKey("Advanced", Boolean.class, "retrieveDiagnostics.gc.enabled", "true", + "Garbage collection on/off switch", true, ConfigKey.Scope.Global); + ConfigKey RetrieveDiagnosticsInterval = new ConfigKey("Advanced", Long.class, "retrieveDiagnostics.gc.interval", "86400", + "Interval between garbage collection execution", true, ConfigKey.Scope.Global); + ConfigKey RetrieveDiagnosticsDisableThreshold = new ConfigKey("Advanced", Float.class, "retrieveDiagnostics.disablethreshold", "0.95", + "Percentage disk space cut-off before API will fail", true, ConfigKey.Scope.Global); + ConfigKey RetrieveDiagnosticsFilePath = new ConfigKey("Advanced", String.class, "retrieveDiagnostics.filepath", "/tmp", + "The path to use on the management server for all temporary data", true, ConfigKey.Scope.Global); + ConfigKey RetrieveDiagnosticsFileAge = new ConfigKey("Advanced", Long.class, "retrieveDiagnostics.max.fileage", "86400", + "The Diagnostics file age in seconds before considered for garbage collection", true, ConfigKey.Scope.Global); + + public RetrieveDiagnosticsServiceImpl() { + _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Diagnostics-GarbageCollector")); + } + + @Override + public boolean configure(final String name, final Map params) throws ConfigurationException { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Initialising configuring values for retrieve diagnostics api : " + name); + } + _configs = _configDao.getConfiguration(); + + _timeOut = RetrieveDiagnosticsTimeOut.value(); + gcEnabled = RetrieveDiagnosticsGCEnable.value(); + interval = RetrieveDiagnosticsInterval.value(); + disableThreshold = RetrieveDiagnosticsDisableThreshold.value(); + filePath = RetrieveDiagnosticsFilePath.value(); + fileAge = RetrieveDiagnosticsFileAge.value(); + + if (params != null) { + params.put(RetrieveDiagnosticsTimeOut.key(), (Long)RetrieveDiagnosticsTimeOut.value()); + params.put(RetrieveDiagnosticsGCEnable.key(), (Boolean)RetrieveDiagnosticsGCEnable.value()); + params.put(RetrieveDiagnosticsInterval.key(), (Long)RetrieveDiagnosticsInterval.value()); + params.put(RetrieveDiagnosticsDisableThreshold.key(), (Float)RetrieveDiagnosticsDisableThreshold.value()); + params.put(RetrieveDiagnosticsFilePath.key(), (String)RetrieveDiagnosticsFilePath.value()); + params.put(RetrieveDiagnosticsFileAge.key(), (Long)RetrieveDiagnosticsFileAge.value()); + + return true; + } + + String value = (String)params.get("install.numthreads"); + final int numInstallThreads = NumbersUtil.parseInt(value, 10); + threadPool = Executors.newFixedThreadPool(numInstallThreads); + + return false; + } + + @Override + public List get(String key) { + List value = allDefaultDiagnosticsTypeKeys.get(key); + return value != null ? value : null; + } + + + protected void loadDiagnosticsDataConfiguration() { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Retrieving diagnostics data values for retrieve diagnostics api : " + getConfigComponentName()); + } + List listVO = _retrieveDiagnosticsDao.retrieveAllDiagnosticsData(); + DiagnosticsKey diagnosticsKey = null; + for (RetrieveDiagnosticsVO vo : listVO) { + if (allDefaultDiagnosticsTypeKeys != null) { + List value = get(vo.getType()); + if (value == null) { + value = new ArrayList<>(); + diagnosticsKey = new DiagnosticsKey(vo.getRole(), vo.getType(), vo.getDefaultValue(), ""); + value.add(diagnosticsKey); + } else { + diagnosticsKey = new DiagnosticsKey(vo.getRole(), vo.getType(), vo.getDefaultValue(), ""); + value.add(diagnosticsKey); + } + allDefaultDiagnosticsTypeKeys.put(vo.getType(), value); + } + } + _diagnosticsDepot.setDiagnosticsKeyHashMap(allDefaultDiagnosticsTypeKeys); + } + + public Pair, Integer> searchForDiagnosticsConfigurations(final RetrieveDiagnosticsCmd cmd) { + final Long zoneId = _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), cmd.getId()); + final Long timeOut = NumbersUtil.parseLong(cmd.getTimeOut(), 3600); + final Object id = _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), cmd.getId()); + + final Filter searchFilter = new Filter(ConfigurationVO.class, "id", true, 0L, 0L); + + final SearchBuilder sb = _configDao.createSearchBuilder(); + sb.and("timeout", sb.entity().getValue(), SearchCriteria.Op.EQ); + final SearchCriteria sc = sb.create(); + if (timeOut != null) { + sc.setParameters("timeout", timeOut); + } + final Pair, Integer> result = _configDao.searchAndCount(sc, searchFilter); + final List configVOList = new ArrayList(); + for (final ConfigurationVO param : result.first()) { + final ConfigurationVO configVo = _configDao.findByName(param.getName()); + if (configVo != null) { + final ConfigKey key = _configDepot.get(param.getName()); + if (key != null) { + configVo.setValue(key.valueIn((Long) id) == null ? null : key.valueIn((Long) id).toString()); + configVOList.add(configVo); + } else { + LOGGER.warn("ConfigDepot could not find parameter " + param.getName()); + } + } else { + LOGGER.warn("Configuration item " + param.getName() + " not found."); + } + } + return new Pair, Integer>(configVOList, configVOList.size()); + } + + private String getVolumeProperty(long volumeId, String property) { + VolumeDetailVO volumeDetails = volumeDetailsDao.findDetail(volumeId, property); + + if (volumeDetails != null) { + return volumeDetails.getValue(); + } + + return null; + } + + private Map getVolumeDetails(VolumeInfo volumeInfo) { + long storagePoolId = volumeInfo.getPoolId(); + StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId); + + if (!storagePoolVO.isManaged()) { + return null; + } + + Map volumeDetails = new HashMap<>(); + + VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId()); + + volumeDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress()); + volumeDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort())); + volumeDetails.put(DiskTO.IQN, volumeVO.get_iScsiName()); + + volumeDetails.put(DiskTO.VOLUME_SIZE, String.valueOf(volumeVO.getSize())); + volumeDetails.put(DiskTO.SCSI_NAA_DEVICE_ID, getVolumeProperty(volumeInfo.getId(), DiskTO.SCSI_NAA_DEVICE_ID)); + + ChapInfo chapInfo = _volumeService.getChapInfo(volumeInfo, volumeInfo.getDataStore()); + + if (chapInfo != null) { + volumeDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername()); + volumeDetails.put(DiskTO.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret()); + volumeDetails.put(DiskTO.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername()); + volumeDetails.put(DiskTO.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); + } + + return volumeDetails; + } + + @Override + public String getDiagnosticsFiles(final RetrieveDiagnosticsCmd cmd) + throws ConcurrentOperationException, InvalidParameterValueException, ConfigurationException { + String systemVmType = null; + String fileDetails = null; + String listOfDiagnosticsFiles = null; + List diagnosticsFiles = new ArrayList<>(); + if (configParams == null) { + configParams = new HashMap<>(); + } + final Long vmId = cmd.getId(); + vmInstance = _vmDao.findByIdTypes(vmId, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.DomainRouter, VirtualMachine.Type.SecondaryStorageVm); + //vmInstance.getDetails() + + if (vmInstance == null) { + throw new InvalidParameterValueException("Unable to find a system VM with id " + vmId); + } + hostId = vmInstance.getHostId(); + if (hostId == null) { + throw new CloudRuntimeException("Unable to find host for virtual machine instance -> " + vmInstance.getInstanceName()); + } + + loadDiagnosticsDataConfiguration(); + if (configure(getConfigComponentName(), configParams)) { + if (cmd != null) { + if (cmd.getTimeOut() != null) { + RetrieveDiagnosticsTimeOut = new ConfigKey("Advanced", Long.class, "", cmd.getTimeOut(), "", true); + } + systemVmType = cmd.getEventType(); + diagnosticsType = cmd.getType(); + if (systemVmType == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "No host was selected."); + } + if (diagnosticsType == null) { + listOfDiagnosticsFiles = getAllDefaultFilesForEachSystemVm(diagnosticsType); + } else { + fileDetails = cmd.getOptionalListOfFiles(); + if (fileDetails != null) { + StringBuilder filesToRetrieve = new StringBuilder(); + filesToRetrieve.append(fileDetails); + listOfDiagnosticsFiles = getDefaultFilesForVm(diagnosticsType, systemVmType); + if (listOfDiagnosticsFiles != null) { + filesToRetrieve.append("," + listOfDiagnosticsFiles); + } + listOfDiagnosticsFiles = filesToRetrieve.toString(); + } else { + //retrieve default files from diagnostics data class for the system vm + listOfDiagnosticsFiles = getDefaultFilesForVm(diagnosticsType, systemVmType); + } + } + String response = null; + try { + /*final Map vmAccessDetail = networkManager.getSystemVMAccessDetails(vmInstance); + String ip = vmAccessDetail.get(NetworkElementCommand.ROUTER_IP);*/ + if (checkForDiskSpace(vmInstance, disableThreshold)) { + response = retrieveDiagnosticsFiles(vmId, listOfDiagnosticsFiles, vmInstance, RetrieveDiagnosticsTimeOut.value()); + if (response != null) { + try { + VolumeVO volume = null; + volume = _volumeDao.findById(hostId); + Long zoneId = volume.getDataCenterId(); + DataStore store = _dataStoreMgr.getImageStore(zoneId); + VolumeInfo volInfo = volumeFactory.getVolume(volume.getId()); + Hypervisor.HypervisorType type = volInfo.getHypervisorType(); + String hostIp = null; + HostVO hostVO = null; + if (Hypervisor.HypervisorType.KVM.equals(type)) { + hostVO = getHost(volInfo.getDataCenterId(), type, true); + hostIp = hostVO.getPublicIpAddress(); + } + RetrieveZipFilesCommand command = new RetrieveZipFilesCommand(false, true); + /*boolean srcVolumeDetached = volInfo.getAttachedVM() == null; + StoragePoolVO storagePoolVO = _storagePoolDao.findById(volInfo.getPoolId()); + Map srcDetails = getVolumeDetails(volInfo); + VolumeObjectTO volTO = new VolumeObjectTO(); + String command = "./scpScript";*/ + Answer copyToHost = _agentMgr.send(hostVO.getId(), command); + if (copyToHost == null && !StringUtils.isEmpty(copyToHost.getDetails())) { + throw new CloudRuntimeException(copyToHost.getDetails()); + } + /*long ssZoneid = hostVO.getDataCenterId(); + + SecondaryStorageVmVO secStorageVm = _secStorageVmDao.findByInstanceName(hostVO.getName()); + if (secStorageVm == null) { + LOGGER.warn("secondary storage VM " + hostVO.getName() + " doesn't exist"); + + } + + DataStore ssStores = _dataStoreMgr.getImageStore(ssZoneid); + VolumeInfo destvol = volumeFactory.getVolume(volume.getId(), ssStores);//(VolumeInfo)ssStores; + String secUrl = ssStores.getUri();*/ + command = new RetrieveZipFilesCommand(true, true); + Answer copyToSec = _agentMgr.send(hostVO.getId(), command); + if (copyToSec != null && !StringUtils.isEmpty(copyToSec.getDetails())) { + throw new CloudRuntimeException(copyToSec.getDetails()); + } + } catch (AgentUnavailableException e) { + + } catch (OperationTimedoutException ex) { + + } + } + /*try { + /* if (!downloadDiagnosticsFileToSecStorage(VirtualMachineProfile profile, DeployDestination dest)) { + return + } + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException("Resources on the secondary storage vm are limited to copy the diagnostics zip file."); + }*/ + } + + } catch(ConcurrentOperationException ex) { + throw new CloudRuntimeException("Unable to retrieve diagnostic files" + ex.getCause()); + } + } + } + return null; + + } + + private DataStore findDataStore(VirtualMachineProfile profile, DeployDestination dest) { + DataStore dataStore = null; + if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) { + if (dest.getStorageForDisks() != null) { + for (final Volume volume : dest.getStorageForDisks().keySet()) { + if (volume.getVolumeType() == Volume.Type.ROOT) { + final StoragePool primaryPool = dest.getStorageForDisks().get(volume); + dataStore = _dataStoreMgr.getDataStore(primaryPool.getId(), DataStoreRole.Primary); + break; + } + } + } + if (dataStore == null) { + final List volumes = _volumeDao.findByInstanceAndType(profile.getVirtualMachine().getId(), Volume.Type.ROOT); + if (volumes != null && volumes.size() > 0) { + dataStore = _dataStoreMgr.getDataStore(volumes.get(0).getPoolId(), DataStoreRole.Primary); + } + } + } else { + dataStore = _dataStoreMgr.getImageStore(dest.getDataCenter().getId()); + } + return dataStore; + } + + private Long findAgentIdForImageStore(final DataStore dataStore) throws ResourceUnavailableException { + EndPoint endpoint = _ep.select(dataStore); + if (endpoint == null) { + throw new ResourceUnavailableException("Config drive creation failed, secondary store not available", + dataStore.getClass(), dataStore.getId()); + } + return endpoint.getId(); + } + + private Long findAgentId(VirtualMachineProfile profile, DeployDestination dest, DataStore dataStore) throws ResourceUnavailableException { + Long agentId; + if (dest.getHost() == null) { + agentId = (profile.getVirtualMachine().getHostId() == null ? profile.getVirtualMachine().getLastHostId() : profile.getVirtualMachine().getHostId()); + } else { + agentId = dest.getHost().getId(); + } + if (!VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) { + agentId = findAgentIdForImageStore(dataStore); + } + return agentId; + } + + private boolean downloadDiagnosticsFileToSecStorage(VirtualMachineProfile profile, DeployDestination dest) throws ResourceUnavailableException { + final DataStore dataStore = findDataStore(profile, dest); + final Long agentId = findAgentId(profile, dest, dataStore); + if (agentId == null || dataStore == null) { + throw new ResourceUnavailableException("Failed to copy diagnostics file, agent or datastore not available", + ConfigDriveNetworkElement.class, 0L); + } + + LOGGER.debug("Copying diagnostics zip file to secondary storage."); + + final String diagnosticsFileName = Diagnostics.diagnosticsFileName(profile.getInstanceName()); + final String diagnosticsPath = Diagnostics.createDiagnosticsPath(profile.getInstanceName()); + final String diagnosticsData = DiagnosticsBuilder.buildConfigDrive(Diagnostics.DIAGNOSTICSDIR, diagnosticsFileName); + final HandleDiagnosticsZipFileCommand diagnosticsZipFileCommand = new HandleDiagnosticsZipFileCommand(diagnosticsPath, diagnosticsData, dataStore.getTO(), true); + + final Answer answer = _agentMgr.easySend(agentId, diagnosticsZipFileCommand); + if (!answer.getResult()) { + throw new ResourceUnavailableException(String.format("Config drive iso creation failed, details: %s", + answer.getDetails()), ConfigDriveNetworkElement.class, 0L); + } + return true; + } + + + private boolean checkAndStartApache() { + //Check whether the Apache server is running + Script command = new Script("/bin/systemctl", LOGGER); + command.add("is-active"); + command.add("apache2"); + String result = command.execute(); + + //Apache Server is not running. Try to start it. + if (result != null && !result.equals("active")) { + command = new Script("/bin/systemctl", LOGGER); + command.add("start"); + command.add("apache2"); + result = command.execute(); + if (result != null) { + LOGGER.warn("Error in starting apache2 service err=" + result); + return false; + } + } + + return true; + } + + private CreateEntityDownloadURLAnswer handleCreateEntityURLCommand(CreateEntityDownloadURLCommand cmd) { + + boolean isApacheUp = checkAndStartApache(); + if (!isApacheUp) { + String errorString = "Error in starting Apache server "; + LOGGER.error(errorString); + return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); + } + // Create the directory structure so that its visible under apache server root + String downloadDir = "/var/www/html/diagnosticsdata/"; + Script command = new Script("/bin/su", LOGGER); + command.add("-s"); + command.add("/bin/bash"); + command.add("-c"); + command.add("mkdir -p " + downloadDir); + command.add("www-data"); + String result = command.execute(); + if (result != null) { + String errorString = "Error in creating directory =" + result; + LOGGER.error(errorString); + return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); + } + + // Create a random file under the directory for security reasons. + String uuid = cmd.getExtractLinkUUID(); + // Create a symbolic link from the actual directory to the template location. The entity would be directly visible under /var/www/html/diagnosticsdata/cmd.getInstallPath(); + command = new Script("/bin/bash", LOGGER); + command.add("-c"); + command.add("ln -sf /mnt/SecStorage/" + cmd.getParent() + File.separator + cmd.getInstallPath() + " " + downloadDir + uuid); + result = command.execute(); + if (result != null) { + String errorString = "Error in linking err=" + result; + LOGGER.error(errorString); + return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); + } + + return new CreateEntityDownloadURLAnswer("", CreateEntityDownloadURLAnswer.RESULT_SUCCESS); + } + + public class Completion implements CompressedFileUploader.UploadCompleteCallback { + private final String jobId; + + public Completion(String jobId) { + this.jobId = jobId; + } + + @Override + public void uploadComplete(CompressedFileUploader.Status status) { + setUploadStatus(jobId, status); + } + } + + public void setUploadStatus(String jobId, CompressedFileUploader.Status status) { + UploadJob uj = jobs.get(jobId); + if (uj == null) { + LOGGER.warn("setUploadStatus for jobId: " + jobId + ", status=" + status + " no job found"); + return; + } + CompressedFileUploader tu = uj.getTemplateUploader(); + LOGGER.warn("Upload Completion for jobId: " + jobId + ", status=" + status); + LOGGER.warn("error=" + tu.getUploadError()); + + switch (status) { + case ABORTED: + case NOT_STARTED: + case UNRECOVERABLE_ERROR: + // Delete the entity only if its a volume. TO DO - find a better way of finding it a volume. + if (uj.getTemplateUploader().getUploadLocalPath().indexOf("volume") > -1) { + uj.cleanup(); + } + break; + case UNKNOWN: + return; + case IN_PROGRESS: + LOGGER.info("Resuming jobId: " + jobId + ", status=" + status); + tu.setResume(true); + threadPool.execute(tu); + break; + case RECOVERABLE_ERROR: + threadPool.execute(tu); + break; + case UPLOAD_FINISHED: + tu.setUploadError("Upload success, starting install "); + String result = postUpload(jobId); + if (result != null) { + LOGGER.error("Failed post upload script: " + result); + tu.setStatus(CompressedFileUploader.Status.UNRECOVERABLE_ERROR); + tu.setUploadError("Failed post upload script: " + result); + } else { + LOGGER.warn("Upload completed successfully at " + new SimpleDateFormat().format(new Date())); + tu.setStatus(CompressedFileUploader.Status.POST_UPLOAD_FINISHED); + tu.setUploadError("Upload completed successfully at " + new SimpleDateFormat().format(new Date())); + } + // Delete the entity only if its a volume. TO DO - find a better way of finding it a volume. + if (uj.getTemplateUploader().getUploadLocalPath().indexOf("volume") > -1) { + uj.cleanup(); + } + break; + default: + break; + } + } + + private String postUpload(String jobId) { + return null; + } + + private static class UploadJob { + private final CompressedFileUploader tu; + + public UploadJob(CompressedFileUploader tu, String jobId, long id, String name, Storage.ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, + String installPathPrefix) { + super(); + this.tu = tu; + } + + public CompressedFileUploader getTemplateUploader() { + return tu; + } + + public void cleanup() { + if (tu != null) { + String upldPath = tu.getUploadLocalPath(); + if (upldPath != null) { + File f = new File(upldPath); + f.delete(); + } + } + } + + } + + private Answer handleDeleteEntityDownloadURLCommand(DeleteEntityDownloadURLCommand cmd) { + + //Delete the soft link. Example path = volumes/8/74eeb2c6-8ab1-4357-841f-2e9d06d1f360.vhd + LOGGER.warn("handleDeleteEntityDownloadURLCommand Path:" + cmd.getPath() + " Type:" + cmd.getType().toString()); + String path = cmd.getPath(); + Script command = new Script("/bin/bash", LOGGER); + command.add("-c"); + + //We just need to remove the UUID.vhd + String extractUrl = cmd.getExtractUrl(); + command.add("unlink /var/www/html/userdata/" + extractUrl.substring(extractUrl.lastIndexOf(File.separator) + 1)); + String result = command.execute(); + if (result != null) { + // FIXME - Ideally should bail out if you cant delete symlink. Not doing it right now. + // This is because the ssvm might already be destroyed and the symlinks do not exist. + LOGGER.warn("Error in deleting symlink :" + result); + } + + // If its a volume also delete the Hard link since it was created only for the purpose of download. + if (cmd.getType() == Upload.Type.VOLUME) { + command = new Script("/bin/bash", LOGGER); + command.add("-c"); + command.add("rm -rf /mnt/SecStorage/" + cmd.getParentPath() + File.separator + path); + LOGGER.warn(" " + parentDir + File.separator + path); + result = command.execute(); + if (result != null) { + String errorString = "Error in deleting volume " + path + " : " + result; + LOGGER.warn(errorString); + return new Answer(cmd, false, errorString); + } + } + + return new Answer(cmd, true, ""); + } + + private HostVO getHost(Long zoneId, Hypervisor.HypervisorType hypervisorType, boolean computeClusterMustSupportResign) { + Preconditions.checkArgument(zoneId != null, "Zone ID cannot be null."); + Preconditions.checkArgument(hypervisorType != null, "Hypervisor type cannot be null."); + + List hosts = _hostDao.listByDataCenterIdAndHypervisorType(zoneId, hypervisorType); + + if (hosts == null) { + return null; + } + + List clustersToSkip = new ArrayList<>(); + + Collections.shuffle(hosts, RANDOM); + + for (HostVO host : hosts) { + + if (!ResourceState.Enabled.equals(host.getResourceState())) { + continue; + } + + if (computeClusterMustSupportResign) { + long clusterId = host.getClusterId(); + + if (clustersToSkip.contains(clusterId)) { + continue; + } + + if (clusterDao.getSupportsResigning(clusterId)) { + return host; + } + else { + clustersToSkip.add(clusterId); + } + } + else { + return host; + } + } + + return null; + } + + + protected String getAllDefaultFilesForEachSystemVm(String diagnosticsType) { + StringBuilder listDefaultFilesForEachVm = new StringBuilder(); + List diagnosticsKey = get(diagnosticsType); + for (DiagnosticsKey key : diagnosticsKey) { + listDefaultFilesForEachVm.append(key.getDetail()); + } + return listDefaultFilesForEachVm.toString(); + } + + protected String getDefaultFilesForVm(String diagnosticsType, String systemVmType) { + String listDefaultFilesForVm = null; + List diagnosticsKey = allDefaultDiagnosticsTypeKeys.get(diagnosticsType); + for (DiagnosticsKey key : diagnosticsKey) { + if (key.getRole().equalsIgnoreCase(systemVmType)) { + listDefaultFilesForVm = key.getDetail(); + return listDefaultFilesForVm; + } + } + return null; + } + + protected void createFolder(String path) { + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + } + + protected String retrieveDiagnosticsFiles(Long ssHostId, String diagnosticsFiles, final VMInstanceVO vmInstance, Long timeout) + throws ConcurrentOperationException { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Retrieving diagnostics files : " + getConfigComponentName()); + } + if (gcEnabled) { + if (!start()) { + LOGGER.info("Failed to start the Diagnostics GarbageCollector"); + } + } + String tempStr = null; + String executionDetail = null; + final Hypervisor.HypervisorType hypervisorType = vmInstance.getHypervisorType(); + List scripts = new ArrayList<>(); + String[] files = diagnosticsFiles.split(","); + List filesToRetrieve = new ArrayList<>(); + for (int i = files.length - 1; i >= 0; i--) { + if (files[i].contains("[") || files[i].contains("]")) { + tempStr = files[i].trim().replaceAll("(^\\[(.*?)\\].*?)", ("$2").trim()); + scriptName = tempStr.toLowerCase().concat(".py"); + scripts.add(scriptName); + } else { + filesToRetrieve.add(files[i]); + } + } + + String details = String.join(" ", filesToRetrieve); + accessDetails = networkManager.getSystemVMAccessDetails(vmInstance); + RetrieveFilesCommand retrieveFilesCommand = new RetrieveFilesCommand(details, vmManager.getExecuteInSequence(hypervisorType)); + Answer retrieveAnswer = null; + retrieveFilesCommand.setAccessDetail(accessDetails); + try { + retrieveAnswer = _agentMgr.send(this.vmInstance.getHostId(), retrieveFilesCommand); + } catch (AgentUnavailableException ex) { + + } catch (OperationTimedoutException e) { + + } + if (retrieveAnswer != null) { + executionDetail = ((RetrieveDiagnosticsAnswer) retrieveAnswer).getOutput(); + } else { + throw new CloudRuntimeException("Failed to execute RetrieveDiagnosticsCommand on remote host: " + retrieveAnswer.getDetails()); + } + + ExecuteScriptCommand execCmd = null; + if (!scripts.isEmpty()) { + for (String script : scripts) { + execCmd = new ExecuteScriptCommand(script, vmManager.getExecuteInSequence(hypervisorType)); + execCmd.setAccessDetail(accessDetails); + try { + retrieveAnswer = _agentMgr.send(hostId, execCmd); + } catch (AgentUnavailableException ex) { + + } catch (OperationTimedoutException e) { + + } + if (retrieveAnswer != null && (retrieveAnswer instanceof RetrieveDiagnosticsAnswer)) { + executionDetail = ((RetrieveDiagnosticsAnswer) retrieveAnswer).getOutput(); + } else { + throw new CloudRuntimeException("Failed to execute ExecuteScriptCommand on remote host: " + retrieveAnswer.getDetails()); + } + } + } + if (Strings.isNullOrEmpty(accessDetails.get(NetworkElementCommand.ROUTER_IP))) { + throw new CloudRuntimeException("Unable to set system vm ControlIP for the system vm with ID -> " + vmInstance); + } + return executionDetail; + } + + private boolean checkForDiskSpace(VirtualMachine systemVmId, Float disableThreshold) { + final VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(systemVmId, null, _offeringDao.findById(systemVmId.getId(), systemVmId.getServiceOfferingId()), null, null); + final List volumes = _volumeDao.findCreatedByInstance(vmProfile.getId()); + boolean usesLocal = false; + VolumeVO localVolumeVO = null; + Long volClusterId = null; + for (final VolumeVO volume : volumes) { + final DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + final DiskProfile diskProfile = new DiskProfile(volume, diskOffering, vmProfile.getHypervisorType()); + if (diskProfile.useLocalStorage()) { + usesLocal = true; + localVolumeVO = volume; + break; + } + } + if (usesLocal) { + StoragePool storagePool = _poolDao.findById(localVolumeVO.getPoolId()); + volClusterId = storagePool.getClusterId(); + if (volClusterId != null) { + if (storagePool.isLocal() || usesLocal) { + if (localVolumeVO.getPath() == null) { + String temp = _configs.get("retrieveDiagnostics.filepath"); + localVolumeVO.setPath(_configs.get(temp)); + } + } + } + } else { + + } + List clusterCapacityTypes = getCapacityTypesAtClusterLevel(); + for (Short capacityType : clusterCapacityTypes) { + List capacity = new ArrayList<>(); + capacity = _capacityDao.findCapacityBy(capacityType.intValue(), null, null, volClusterId); + if (capacityType.compareTo(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED) != 0) { + continue; + } + double totalCapacity = capacity.get(0).getTotalCapacity(); + double usedCapacity = capacity.get(0).getUsedCapacity() + capacity.get(0).getReservedCapacity(); + if (totalCapacity != 0 && usedCapacity / totalCapacity > disableThreshold) { + LOGGER.error("Unlimited disk space on primary storage."); + return false; + } + } + return true; + } + + private List getCapacityTypesAtClusterLevel() { + List clusterCapacityTypes = new ArrayList(); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_CPU); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_MEMORY); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_STORAGE); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_LOCAL_STORAGE); + return clusterCapacityTypes; + } + + public Map getConfigParams() { + return configParams; + } + + public void setConfigParams(Map configParams) { + this.configParams = configParams; + } + + @Override + public String getConfigComponentName() { + return RetrieveDiagnosticsServiceImpl.class.getSimpleName(); + } + + public Map> getDefaultDiagnosticsData() { + return allDefaultDiagnosticsTypeKeys; + } + + @Override + public ConfigKey[] getConfigKeys() + { + return new ConfigKey[] { RetrieveDiagnosticsTimeOut, RetrieveDiagnosticsTimeOut, RetrieveDiagnosticsGCEnable, + RetrieveDiagnosticsInterval, RetrieveDiagnosticsDisableThreshold, RetrieveDiagnosticsFileAge }; + } + + + @Override + public List> getCommands(){ + List> cmdList = new ArrayList>(); + cmdList.add(RetrieveDiagnosticsCmd.class); + return cmdList; + } + + protected class DiagnosticsGarbageCollector implements Runnable { + private String diagnosticsZipFileName = null; + private String path = "/diagnosticsdata/"; + + public DiagnosticsGarbageCollector(String diagnosticsZipFileName) { + this.diagnosticsZipFileName = diagnosticsZipFileName; + } + + @Override + public void run() { + try { + LOGGER.trace("Diagnostics Garbage Collection Thread is running."); + File diagnosticszipFile = new File(path + diagnosticsZipFileName); + Path diagnosticsZipFilePath = diagnosticszipFile.toPath(); + BasicFileAttributes attributes = null; + try { + attributes = Files.readAttributes(diagnosticsZipFilePath, BasicFileAttributes.class); + } catch (IOException exception) { + LOGGER.error("Could not find the zip file.", exception); + } + long milliseconds = attributes.creationTime().to(TimeUnit.MILLISECONDS); + VolumeVO volume = null; + volume = _volumeDao.findById(hostId); + Long zoneId = volume.getDataCenterId(); + DataStore store = _dataStoreMgr.getImageStore(zoneId); + DataStoreTO destStore = store.getTO(); + + VolumeObjectTO volTO = new VolumeObjectTO(); + volTO.setDataStore(store.getTO()); + + if (store == null) { + throw new CloudRuntimeException("cannot find an image store for zone " + zoneId); + } + //DataStoreTO destStoreTO = store.getTO(); + if (milliseconds > RetrieveDiagnosticsFileAge.value()) { + String result = cleanupDiagnostics(); + if (result != null) { + String msg = "Unable to delete diagnostics zip file: " + result; + LOGGER.error(msg); + } + } + + } catch (Exception e) { + LOGGER.error("Caught the following Exception", e); + } + } + } + + protected String cleanupDiagnostics() { + Script command = new Script("/bin/bash", LOGGER); + command.add("-c"); + command.add("rm -rf " + secondaryUrl); + CopyRetrieveZipFilesCommand diagnosticsCleanup = new CopyRetrieveZipFilesCommand(command.toString(), "", true, true); + try { + Answer answer = _agentMgr.send(vmInstance.getHostId(), diagnosticsCleanup); + if (answer == null || !answer.getResult()) { + if (answer != null && !StringUtils.isEmpty(answer.getDetails())) { + throw new CloudRuntimeException(answer.getDetails()); + } + } + return answer.getDetails(); + }catch (AgentUnavailableException ex) { + throw new CloudRuntimeException("Agent not available."); + } catch (OperationTimedoutException e) { + throw new CloudRuntimeException("operation timed out."); + } + } + + @Override + public boolean start() { + if (interval == null) { + interval = 86400L; + } + Integer cleanupInterval = (int)(long) interval; + if (!super.start()) { + return false; + } + if (gcEnabled.booleanValue()) { + Random generator = new Random(); + int initialDelay = generator.nextInt(cleanupInterval); + _executor.scheduleWithFixedDelay(new DiagnosticsGarbageCollector(diagnosticsZipFileName), initialDelay, cleanupInterval.intValue(), TimeUnit.SECONDS); + } else { + LOGGER.debug("Diagnostics garbage collector is not enabled, so the cleanup thread is not being scheduled."); + } + + return true; + } + + @Override + public boolean stop() { + if (gcEnabled.booleanValue()) { + _executor.shutdown(); + } + return true; + } + +} diff --git a/server/src/main/java/org/apache/cloudstack/diagnostics/UploadCompressedFileToSecStorage.java b/server/src/main/java/org/apache/cloudstack/diagnostics/UploadCompressedFileToSecStorage.java new file mode 100644 index 000000000000..ac3e2e59d051 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/diagnostics/UploadCompressedFileToSecStorage.java @@ -0,0 +1,142 @@ +package org.apache.cloudstack.diagnostics; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import org.apache.log4j.Logger; + +public class UploadCompressedFileToSecStorage implements CompressedFileUploader { + public static final Logger s_logger = Logger.getLogger(UploadCompressedFileToSecStorage.class.getName()); + public CompressedFileUploader.Status status = CompressedFileUploader.Status.NOT_STARTED; + public String errorString = ""; + public long totalBytes = 0; + public long entitySizeinBytes; + private String sourcePath; + private String ftpUrl; + private UploadCompleteCallback completionCallback; + private BufferedInputStream inputStream = null; + private BufferedOutputStream outputStream = null; + private static final int CHUNK_SIZE = 1024 * 1024; //1M + + public UploadCompressedFileToSecStorage(String sourcePath, String url, UploadCompleteCallback callback, long entitySizeinBytes) { + + this.sourcePath = sourcePath; + ftpUrl = url; + completionCallback = callback; + this.entitySizeinBytes = entitySizeinBytes; + + } + + @Override + public String upload(UploadCompleteCallback callback) { + + switch (status) { + case ABORTED: + case UNRECOVERABLE_ERROR: + case UPLOAD_FINISHED: + return null; + default: + + } + StringBuffer sb = new StringBuffer(ftpUrl); + sb.append(";type=i"); + + try { + URL url = new URL(sb.toString()); + URLConnection urlc = url.openConnection(); + File sourceFile = new File(sourcePath); + entitySizeinBytes = sourceFile.length(); + + outputStream = new BufferedOutputStream(urlc.getOutputStream()); + inputStream = new BufferedInputStream(new FileInputStream(sourceFile)); + + status = UploadCompressedFileToSecStorage.Status.IN_PROGRESS; + + int bytes = 0; + byte[] block = new byte[CHUNK_SIZE]; + boolean done = false; + while (!done && status != Status.ABORTED) { + if ((bytes = inputStream.read(block, 0, CHUNK_SIZE)) > -1) { + outputStream.write(block, 0, bytes); + totalBytes += bytes; + } else { + done = true; + } + } + status = UploadCompressedFileToSecStorage.Status.UPLOAD_FINISHED; + return ftpUrl; + } catch (MalformedURLException e) { + status = UploadCompressedFileToSecStorage.Status.UNRECOVERABLE_ERROR; + errorString = e.getMessage(); + s_logger.error(errorString); + } catch (IOException e) { + status = UploadCompressedFileToSecStorage.Status.UNRECOVERABLE_ERROR; + errorString = e.getMessage(); + s_logger.error(errorString); + } finally { + try { + if (inputStream != null) { + inputStream.close(); + } + if (outputStream != null) { + outputStream.close(); + } + } catch (IOException ioe) { + s_logger.error(" Caught exception while closing the resources"); + } + if (callback != null) { + callback.uploadComplete(status); + } + } + + return null; + } + + @Override + public void setResume(boolean resume) { + + } + + @Override + public void run() { + try { + upload(completionCallback); + } catch (Throwable t) { + s_logger.warn("Caught exception during upload " + t.getMessage(), t); + errorString = "Failed to download: " + t.getMessage(); + status = UploadCompressedFileToSecStorage.Status.UNRECOVERABLE_ERROR; + } + + } + + @Override + public Status getStatus() { + return status; + } + + @Override + public String getUploadError() { + return errorString; + } + + @Override + public String getUploadLocalPath() { + return sourcePath; + } + + @Override + public void setStatus(Status status) { + this.status = status; + } + + @Override + public void setUploadError(String string) { + errorString = string; + } + +} diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index c7715a866c4f..e4432b9b80c1 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -298,4 +298,7 @@ + + + diff --git a/server/src/test/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsServiceImplTest.java new file mode 100644 index 000000000000..d8510894a6b8 --- /dev/null +++ b/server/src/test/java/org/apache/cloudstack/diagnostics/RetrieveDiagnosticsServiceImplTest.java @@ -0,0 +1,229 @@ +/* + * // 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.Command; +import com.cloud.agent.api.ExecuteScriptCommand; +import com.cloud.agent.api.RetrieveFilesCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.manager.Commands; +import com.cloud.agent.resource.virtualnetwork.VRScripts; +import com.cloud.configuration.ConfigurationManagerImpl; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.VMInstanceDao; +import junit.framework.TestCase; +import org.apache.cloudstack.api.command.admin.diagnostics.RetrieveDiagnosticsCmd; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.DiagnosticsKey; +import org.apache.cloudstack.framework.config.impl.RetrieveDiagnosticsVO; +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Assert; +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.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +@RunWith(MockitoJUnitRunner.class) +public class RetrieveDiagnosticsServiceImplTest extends TestCase { + private static final Logger LOGGER = Logger.getLogger(RetrieveDiagnosticsServiceImplTest.class); + + @Mock + private AgentManager _agentManager; + @Mock + private VMInstanceDao instanceDao; + @Mock + private RetrieveDiagnosticsCmd retrieveDiagnosticsCmd; + @Mock + private VMInstanceVO instanceVO; + @Mock + private NetworkOrchestrationService networkManager; + + @InjectMocks + private RetrieveDiagnosticsServiceImpl retrieveDiagnosticsService = new RetrieveDiagnosticsServiceImpl(); + @InjectMocks + ConfigurationManagerImpl configurationMgr = new ConfigurationManagerImpl(); + @Mock + RetrieveDiagnosticsVO retrieveDiagnosticsVO; + @Mock + RetrieveDiagnosticsCmd diagnosticsCmd = new RetrieveDiagnosticsCmd(); + @Mock + Commands commands = new Commands(Command.OnError.Stop); + @Mock + ExecuteScriptCommand executeScriptCommand = new ExecuteScriptCommand(VRScripts.RETRIEVE_DIAGNOSTICS, true ); + + private final String msCSVList = "/var/log/agent.log,/usr/data/management.log,/cloudstack/cloud.log,[IPTABLES]"; + private final List msListDiagnosticsFiles = Arrays.asList(msCSVList.replace(" ","").split(",")); + ConfigKey RetrieveDiagnosticsTimeOut = new ConfigKey("Advanced", Long.class, "retrieveDiagnostics.retrieval.timeout", "3600", + "The timeout setting in seconds for the overall API call", true, ConfigKey.Scope.Global); + private final String type = "LOGFILES"; + @Mock + RetrieveFilesCommand retrieveFilesCommand = new RetrieveFilesCommand(msCSVList, true); + + @Before + public void setUp() throws Exception { + Mockito.when(retrieveDiagnosticsCmd.getId()).thenReturn(2L); + Mockito.when(retrieveDiagnosticsCmd.getType()).thenReturn("LOGFILES"); + 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(retrieveDiagnosticsCmd); + Mockito.reset(_agentManager); + Mockito.reset(instanceDao); + Mockito.reset(instanceVO); + } + + @Test + public void runConfigureTest() throws Exception { + TransactionLegacy txn = TransactionLegacy.open("runConfigureTest"); + ConfigurationDao _configDao = mock(ConfigurationDao.class); + when(_configDao.findById((Long.toString((anyLong()))))).thenReturn(null); + try { + _configDao.getValue(RetrieveDiagnosticsTimeOut.toString()); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Unable to find timeout value")); + } finally { + txn.close("runConfigureTest"); + } + + } + + @Test + public void runGetListOfDiagnosticsKeyClassesTest() throws Exception { + RetrieveDiagnosticsServiceImpl diagnosticsService = new RetrieveDiagnosticsServiceImpl(); + try { + Map> allDefaultDiagnosticsTypeKeys = diagnosticsService.getDefaultDiagnosticsData(); + Assert.assertNotNull(allDefaultDiagnosticsTypeKeys); + } catch (Exception e) { + LOGGER.info("exception in testing runGetListOfDiagnosticsKeyClassesTest message: " + e.toString()); + } finally { + + } + } + + @Test + public void runLoadDiagnosticsDataConfigurationTest() throws Exception { + RetrieveDiagnosticsServiceImpl diagnosticsService = mock(RetrieveDiagnosticsServiceImpl.class); + Map> defaultDiagnosticsTypeKeys = new HashMap<>(); + when(diagnosticsService.getDefaultDiagnosticsData()).thenReturn(defaultDiagnosticsTypeKeys); + } + + @Test + public void runGetAllDefaultFilesForEachSystemVmTest() throws Exception { + final String msCSVList = "agent.log,management.log,cloud.log"; + final String[] msList = msCSVList.split(","); + final String list = msList.toString(); + RetrieveDiagnosticsVO retrieveDiagnosticsVOMock = mock(RetrieveDiagnosticsVO.class); + RetrieveDiagnosticsServiceImpl diagnosticsService = mock(RetrieveDiagnosticsServiceImpl.class); + when(diagnosticsService.getAllDefaultFilesForEachSystemVm(retrieveDiagnosticsVOMock.getType())).thenReturn(list); + assertTrue(msList != null); + } + + @Test + public void runGetDefaultFilesForVmTest() throws Exception { + RetrieveDiagnosticsServiceImpl diagnosticsService = new RetrieveDiagnosticsServiceImpl(); + DiagnosticsKey key = new DiagnosticsKey("ConsoleProxy", "LOGFILES", "agent.log. management.log,cloud.log", ""); + try { + + String allDefaultDiagnosticsTypeKeys = diagnosticsService.getDefaultFilesForVm(key.getDiagnosticsClassType(), "myVm"); + Assert.assertNotNull(allDefaultDiagnosticsTypeKeys); + } catch (Exception e) { + LOGGER.info("exception in testing runGetDefaultFilesForVmTest message: " + e.toString()); + } finally { + + } + } + + @Test + public void runRetrieveDiagnosticsFilesTrueTest() throws Exception { + Mockito.when(retrieveDiagnosticsCmd.getType()).thenReturn(type); + Mockito.when(retrieveDiagnosticsCmd.getOptionalListOfFiles()).thenReturn(msCSVList); + Map accessDetailsMap = new HashMap<>(); + accessDetailsMap.put(NetworkElementCommand.ROUTER_IP, "192.20.120.12"); + Mockito.when(networkManager.getSystemVMAccessDetails(Mockito.any(VMInstanceVO.class))).thenReturn(accessDetailsMap); + final String details = "Copied files : /var/log/agent.log," + + "/usr/data/management.log," + + "/cloudstack/cloud.log"; + retrieveFilesCommand = new RetrieveFilesCommand(details, true); + executeScriptCommand = new ExecuteScriptCommand(VRScripts.RETRIEVE_DIAGNOSTICS, true); + + Commands commands = new Commands(Command.OnError.Stop); + commands.addCommand(retrieveFilesCommand); + commands.addCommand(executeScriptCommand); + + String stdout = "URL of output file : file://apache.org/temp"; + + final Answer[] answers = {new Answer(Mockito.any(RetrieveFilesCommand.class))}; + Mockito.when(_agentManager.send(Mockito.anyLong(), commands)).thenReturn(answers); + + RetrieveDiagnosticsServiceImpl diagnosticsService = new RetrieveDiagnosticsServiceImpl(); + try { + String allDefaultDiagnosticsTypeKeys = diagnosticsService.getDefaultFilesForVm("SecondaryStorageVm", "myVm"); + Assert.assertNotNull(allDefaultDiagnosticsTypeKeys); + } catch (Exception e) { + LOGGER.info("exception in testing runRetrieveDiagnosticsFilesTest message: " + e.toString()); + } finally { + + } + + Map resultsMap = new HashMap<>();//retrieveDiagnosticsService.getDiagnosticsFiles(retrieveDiagnosticsCmd); + resultsMap.put("STDERR", "Error"); + resultsMap.put("STDOUT", "output"); + resultsMap.put("EXITCODE", "0"); + assertEquals(3, resultsMap.size()); + } + +/* + @Test + public void testGarbageCollectStartExecution() throws Exception { + RetrieveDiagnosticsServiceImpl diagnosticsServiceMock = mock(RetrieveDiagnosticsServiceImpl.class); + when(diagnosticsServiceMock.start()).thenReturn(true); + Whitebox.setInternalState(retrieveDiagnosticsService, "RetrieveDiagnosticsFileAge", diagnosticsServiceMock); + + try { + retrieveDiagnosticsService.start(); + } catch (NullPointerException e) { + fail(); + } + } +*/ + +} diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java index 46fa1707a03c..f4b4825538f2 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java @@ -53,6 +53,7 @@ import javax.naming.ConfigurationException; +import com.cloud.agent.api.HandleDiagnosticsZipFileCommand; import org.apache.cloudstack.framework.security.keystore.KeystoreManager; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CopyCommand; @@ -65,6 +66,8 @@ import org.apache.cloudstack.storage.command.UploadStatusCommand; import org.apache.cloudstack.storage.configdrive.ConfigDrive; import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder; +import org.apache.cloudstack.storage.configdrive.org.apache.cloudstack.storage.diagnostics.Diagnostics; +import org.apache.cloudstack.storage.configdrive.org.apache.cloudstack.storage.diagnostics.DiagnosticsBuilder; import org.apache.cloudstack.storage.template.DownloadManager; import org.apache.cloudstack.storage.template.DownloadManagerImpl; import org.apache.cloudstack.storage.template.DownloadManagerImpl.ZfsPathParser; @@ -315,7 +318,9 @@ public Answer executeRequest(Command cmd) { } else if (cmd instanceof UploadStatusCommand) { return execute((UploadStatusCommand)cmd); } else if (cmd instanceof HandleConfigDriveIsoCommand) { - return execute((HandleConfigDriveIsoCommand)cmd); + return execute((HandleConfigDriveIsoCommand) cmd); + } else if (cmd instanceof HandleDiagnosticsZipFileCommand) { + return execute((HandleDiagnosticsZipFileCommand) cmd); } else if (cmd instanceof GetDatadisksCommand) { return execute((GetDatadisksCommand)cmd); } else if (cmd instanceof CreateDatadiskTemplateCommand) { @@ -325,6 +330,27 @@ public Answer executeRequest(Command cmd) { } } + private Answer execute(HandleDiagnosticsZipFileCommand command) { + if (command.isCreate()) { + if (command.getDiagnosticsFileData() == null) { + return new Answer(command, false, "Invalid diagnostics compressed file"); + } + String nfsMountPoint = getRootDir(command.getDestStore().getUrl(), _nfsVersion); + File diagnosticsZipFile = new File(nfsMountPoint, command.getDiagnosticsFile()); + if (!diagnosticsZipFile.exists()) { + Path tempDir = null; + try { + tempDir = java.nio.file.Files.createTempDirectory(Diagnostics.DIAGNOSTICSDIR); + File tmpDiagnosticsFile = DiagnosticsBuilder.base64StringToFile(command.getDiagnosticsFileData(), tempDir.toAbsolutePath().toString(), command.getDiagnosticsFile()); + copyMgtServerZipToNfs(tmpDiagnosticsFile, new File(command.getDiagnosticsFile()), command.getDestStore()); + } catch (IOException | ConfigurationException e) { + return new Answer(command, false, "Failed due to exception: " + e.getMessage()); + } + } + } + return null; + } + private Answer execute(HandleConfigDriveIsoCommand cmd) { if (cmd.isCreate()) { if (cmd.getIsoData() == null) { @@ -371,6 +397,34 @@ private Answer execute(HandleConfigDriveIsoCommand cmd) { } } + protected void copyMgtServerZipToNfs(File localFile, File diagnosticsZipFile, DataStoreTO destData) throws ConfigurationException, IOException { + String scriptsDir = "scripts/storage/secondary"; + String createVolScr = Script.findScript(scriptsDir, "createvolume.sh"); + if (createVolScr == null) { + throw new ConfigurationException("Unable to find createvolume.sh"); + } + s_logger.info("createvolume.sh found in " + createVolScr); + + int installTimeoutPerGig = 180 * 60 * 1000; + int imgSizeGigs = (int) Math.ceil(localFile.length() * 1.0d / (1024 * 1024 * 1024)); + imgSizeGigs++; // add one just in case + long timeout = imgSizeGigs * installTimeoutPerGig; + + Script scr = new Script(createVolScr, timeout, s_logger); + scr.add("-s", Integer.toString(imgSizeGigs)); + scr.add("-n", diagnosticsZipFile.getName()); + scr.add("-t", getRootDir(destData.getUrl(), _nfsVersion) + "/" + diagnosticsZipFile.getParent()); + scr.add("-f", localFile.getAbsolutePath()); + scr.add("-d", "diagnosticsdata"); + String result; + result = scr.execute(); + + if (result != null) { + // script execution failure + throw new CloudRuntimeException("Failed to run script " + createVolScr); + } + } + protected void copyLocalToNfs(File localFile, File isoFile, DataStoreTO destData) throws ConfigurationException, IOException { String scriptsDir = "scripts/storage/secondary"; String createVolScr = Script.findScript(scriptsDir, "createvolume.sh"); diff --git a/systemvm/debian/opt/cloud/bin/diagnostics_files.py b/systemvm/debian/opt/cloud/bin/diagnostics_files.py new file mode 100755 index 000000000000..f7703b03b9e4 --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/diagnostics_files.py @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# 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. +class FindFiles(object): + argCount = 0 + arguments = None + + def __init__(self): + self.arguments = sys.argv[1:] + + def copy_and_compress_files(self): + number = 0 + timestr = time.strftime("%Y%m%d-%H%M%S") + zipFileName = "/tmp/diagnosticsFiles_" + timestr + ".zip" + #print "Zip file name = " + zipFileName + #print "arguments " + ", ".join(sys.argv[1:]) + #arguments = sys.argv + for file_name in sys.argv[1:]: + if os.path.isfile(file_name): + zip_archive = ZipFile(zipFileName, "a") + zip_archive.write(file_name) + zip_archive.close() + print("All diagnostics files zipped successfully: %s", ",".join(sys.argv[1:])) + + def ensure_dir(self, filepath): + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + try: + p = sp.Popen(shlex.split("mkdir -p temp"), stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) + stdout, stderr = p.communicate() + except OSError as e: + print("Failed to create directory temp") + + +if __name__ == "__main__": + file_path = "/tmp/" + find_files = FindFiles(sys.argv[1:]) +# find_files.ensure_dir(file_path) +# for file_name in sys.argv[1:]: + find_files.copy_and_compress_files() + + + + diff --git a/systemvm/debian/opt/cloud/bin/ifconfig.py b/systemvm/debian/opt/cloud/bin/ifconfig.py new file mode 100644 index 000000000000..f979d71fbdd2 --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/ifconfig.py @@ -0,0 +1,61 @@ +#!/usr/bin/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 os +import sys +import time + +from zipfile import ZipFile + +import subprocess as sp +import shlex + + +class SaveIfconfigToLogFile: + + def __init__(self): + self.arguments = None + + def saveIfconfigEntries(self,dest): + name = "/tmp/ifconfig.log" + command = 'iptables-save > /tmp/iptables.log' + os.system(command) + timestr = time.strftime("%Y%m%d-%H%M%S") + zipFileName = "/tmp/diagnosticsFiles_" + timestr + ".zip" + print "Zip file name = " + zipFileName + zip_archive = ZipFile(zipFileName, "a") + zip_archive.write(name) + zip_archive.close() + print("All diagnostics files zipped successfully") + + def ensure_dir(self, filepath): + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + try: + p = sp.Popen(shlex.split("mkdir -p temp"), stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) + stdout, stderr = p.communicate() + except OSError as e: + print("Failed to create directory temp") + + +if __name__ == "__main__": + # arguments = sys.argv + file_path = "/tmp/" + save_files = SaveIfconfigToLogFile() + #save_files.ensure_dir(file_path) + save_files.saveIfconfigEntries(file_path) diff --git a/systemvm/debian/opt/cloud/bin/iptables.py b/systemvm/debian/opt/cloud/bin/iptables.py new file mode 100644 index 000000000000..0808cd9b0fd8 --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/iptables.py @@ -0,0 +1,67 @@ +#!/usr/bin/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 os +import sys +import time + +from zipfile import ZipFile + +import subprocess as sp +import shlex + +class SaveIptablesToLogFile: + + def __init__(self): + self.arguments = None + + def saveIpTableEntries(self,dest): + name = "/tmp/iptables.log" + command = 'ifconfig > /tmp/iptables.log' + os.system(command) + timestr = time.strftime("%Y%m%d-%H%M%S") + zipFileName = "/tmp/diagnosticsFiles_" + timestr + ".zip" + print "Zip file name = " + zipFileName + zip_archive = ZipFile(zipFileName, "a") +# if os.path.isfile(name): + zip_archive.write(name) +# else: +# print name + " not found." + zip_archive.close() + print("All diagnostics files zipped successfully") + + def ensure_dir(self, filepath): + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + try: + p = sp.Popen(shlex.split("mkdir -p temp"), stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) + stdout, stderr = p.communicate() + except OSError as e: + print("Failed to create directory temp") + + +if __name__ == "__main__": + # arguments = sys.argv + file_path = "/tmp/" + save_files = SaveIptablesToLogFile() + #save_files.ensure_dir(file_path) + save_files.saveIpTableEntries(file_path) + + + + diff --git a/systemvm/debian/opt/cloud/bin/retrieve_diagnostics.py b/systemvm/debian/opt/cloud/bin/retrieve_diagnostics.py new file mode 100755 index 000000000000..e0a114e0f09a --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/retrieve_diagnostics.py @@ -0,0 +1,81 @@ +#!/usr/bin/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. +#!/usr/bin/python +import sys +import subprocess as sp +import shlex + +def executeCmd(command): + commaArg = command.split(" ") + try: + cmd = "./retrieve_files.py " + commaArg + p = sp.Popen(shlex.split(cmd), stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) + stdout, stderr = p.communicate() + return_code = p.returncode + except OSError as e: + print("Failed to append files." + e.message) + finally: + print("Return code : %d", return_code) + +def getCommand(): + arguments = sys.argv + cmd = " ".join(arguments[1:]) + return cmd + + +if __name__ == "__main__": + command = getCommand() + executeCmd(command) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systemvm/debian/opt/cloud/bin/retrieve_files.py b/systemvm/debian/opt/cloud/bin/retrieve_files.py new file mode 100644 index 000000000000..35bb14934fde --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/retrieve_files.py @@ -0,0 +1,38 @@ +#!/usr/bin/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. +#!/usr/bin/python + +import sys +import os +import time + +from zipfile import ZipFile + +def get_files(): + timestr = time.strftime("%Y%m%d-%H%M%S") + zipFileName = "/tmp/diagnosticsFiles_" + timestr + ".zip" + arguments = sys.argv + for file in arguments: + if os.path.isfile(file): + zip_archive = ZipFile(zipFileName, "a") + zip_archive.write(file) + zip_archive.close() + + +if __name__ == "__main__": + get_files() diff --git a/systemvm/debian/opt/cloud/bin/retrievediagnostics_cleanup.py b/systemvm/debian/opt/cloud/bin/retrievediagnostics_cleanup.py new file mode 100644 index 000000000000..1fb4729bcca9 --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/retrievediagnostics_cleanup.py @@ -0,0 +1,29 @@ +#!/usr/bin/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. +#!/usr/bin/python +import os + +class FindFiles(object): + + def cleanup(self): + os.remove("/opt/cloud/bin/temp/*.zip") + + +if __name__ == "__main__": + find_files = FindFiles() + find_files.cleanup() diff --git a/systemvm/debian/opt/cloud/bin/route.py b/systemvm/debian/opt/cloud/bin/route.py new file mode 100644 index 000000000000..ece8668d402f --- /dev/null +++ b/systemvm/debian/opt/cloud/bin/route.py @@ -0,0 +1,60 @@ +#!/usr/bin/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 os +import sys +import time + +from zipfile import ZipFile + +import subprocess as sp +import shlex + +class SaveRoutetablesToLogFile: + + def __init__(self): + self.arguments = None + + def saveRouteTableEntries(self): + name = "/tmp/route.log" + command = 'netstat -rn > /tmp/route.log' + os.system(command) + timestr = time.strftime("%Y%m%d-%H%M%S") + zipFileName = "/tmp/diagnosticsFiles_" + timestr + ".zip" + print "Zip file name = " + zipFileName + zip_archive = ZipFile(zipFileName, "a") + zip_archive.write(name) + zip_archive.close() + print("All diagnostics files zipped successfully") + + def ensure_dir(self, filepath): + directory = os.path.dirname(file_path) + if not os.path.exists(directory): + try: + p = sp.Popen(shlex.split("mkdir -p temp"), stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE) + stdout, stderr = p.communicate() + except OSError as e: + print("Failed to create directory temp") + + +if __name__ == "__main__": +# arguments = sys.argv + file_path = "/tmp/" + save_files = SaveRoutetablesToLogFile() + #save_files.ensure_dir(file_path) + save_files.saveRouteTableEntries() diff --git a/test/integration/smoke/test_retrieve_diagnostics.py b/test/integration/smoke/test_retrieve_diagnostics.py new file mode 100644 index 000000000000..cc67dfeb083d --- /dev/null +++ b/test/integration/smoke/test_retrieve_diagnostics.py @@ -0,0 +1,235 @@ +# 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 retrieve diagnostics of system VMs +""" +# Import Local Modules +from marvin.codes import FAILED +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import retrieveDiagnostics +from marvin.lib.utils import (cleanup_resources) +from marvin.lib.base import (Account, + ServiceOffering, + VirtualMachine, + Configurations) +from marvin.lib.common import (get_domain, + get_zone, + get_test_template, + list_ssvms, + list_routers) + +from nose.plugins.attrib import attr + +class TestRetrieveDiagnostics(cloudstackTestCase): + """ + Test retrieve diagnostics with system VMs and VR as root admin + """ + + @classmethod + def setUpClass(cls): + + testClient = super(TestRetrieveDiagnostics, 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, + cls.vm_1 + ] + + @classmethod + def tearDownClass(cls): + try: + cls.apiclient = super( + TestRetrieveDiagnostics, + 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.dbClient = self.testClient.getDbConnection() + self.hypervisor = self.testClient.getHypervisorInfo() + + @attr(tags=["advanced", "smoke", "eip", "advancedns", "sg"], required_hardware="true") + def test_ssvm_retrieve_files_success(self): + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmId='secondarystoragevm', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_ssvm_response[0] + cmd = retrieveDiagnostics.retrieveDiagnosticsCmd() + cmd.systemvmId = ssvm.id + cmd.type = "LOGFILES" + cmd.details = "/var/log/cloudstack/agent/agent.log,/var/log/yum.log,/var/log/cloudstack/agent/security_group.log" + response = self.apiclient.getDiagnosticsFiles(cmd) + self.assertEqual(len(response), 1) + self.assertEqual(response[0].name, 'url') + + @attr(tags=["advanced", "smoke", "eip", "advancedns", "sg"], required_hardware="true") + def test_router_retrieve_files_success(self): + list_router_response = list_ssvms( + self.apiclient, + systemvmId='domainrouter', + state='Running', + ) + + self.assertEqual( + isinstance(list_router_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_router_response[0] + cmd = retrieveDiagnostics.retrieveDiagnosticsCmd() + cmd.systemvmId = ssvm.id + cmd.type = "LOGFILES" + cmd.details = "/var/log/cloudstack/agent/agent.log,/var/log/cloudstack/agent/security_group.log,[IPTABLES]" + response = self.apiclient.getDiagnosticsFiles(cmd) + self.assertEqual(len(response), 1) + self.assertEqual(response[0].name, 'Log file downloaded successfully') + + @attr(tags=["advanced", "smoke", "eip", "advancedns", "sg"], required_hardware="true") + def test_cpvm_retrieve_files_success(self): + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmId='consoleproxy', + state='Running', + ) + + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_cpvm_response[0] + cmd = retrieveDiagnostics.retrieveDiagnosticsCmd() + cmd.systemvmId = ssvm.id + cmd.type = "LOGFILES" + cmd.details = "/var/log/cloudstack/agent/agent.log,/var/log/cloudstack/agent/security_group.log,[IPTABLES]" + response = self.apiclient.getDiagnosticsFiles(cmd) + self.assertEqual(len(response), 1) + self.assertEqual(response[0].name, 'Log file downloaded successfully') + + @attr(tags=["advanced", "smoke", "eip", "advancedns", "sg"], required_hardware="true") + def test_ssvm_retrieve_files_failure(self): + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmId='consoleproxy', + state='Running', + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_ssvm_response[0] + cmd = retrieveDiagnostics.retrieveDiagnosticsCmd() + cmd.systemvmId = ssvm.id + cmd.type = "FILES" + cmd.details = "agent.log,cloud.log" + response = self.apiclient.getDiagnosticsFiles(cmd) + self.assertEqual(len(response), 1) + self.assertEqual(response[0].name, 'Failed to locate files from the system vm, check if the directory specified is correct.') + + @attr(tags=["advanced", "smoke", "eip", "advancedns", "sg"], required_hardware="true") + def test_router_retrieve_files_failure(self): + list_router_response = list_ssvms( + self.apiclient, + systemvmId='domainrouter', + state='Running', + ) + + self.assertEqual( + isinstance(list_router_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_router_response[0] + cmd = retrieveDiagnostics.retrieveDiagnosticsCmd() + cmd.systemvmId = ssvm.id + cmd.type = "FILES" + cmd.details = "[IPTABLES],[IFCONFIG]" + response = self.apiclient.getDiagnosticsFiles(cmd) + self.assertEqual(len(response), 1) + self.assertEqual(response[0].name, 'Diagnostic type specified is not supported.') + + @attr(tags=["advanced", "smoke", "eip", "advancedns", "sg"], required_hardware="true") + def test_cpvm_retrieve_files_failure(self): + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmId='testingvm', + state='Running', + ) + + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + 'Check list response returns a valid list' + ) + ssvm = list_cpvm_response[0] + cmd = retrieveDiagnostics.retrieveDiagnosticsCmd() + cmd.systemvmId = ssvm.id + cmd.type = "FILES" + cmd.details = "[IPTABLES],[IFCONFIG]" + response = self.apiclient.getDiagnosticsFiles(cmd) + self.assertEqual(len(response), 1) + self.assertEqual(response[0].name, 'Failed to find the system vm specified.') diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index a025efefcbcb..aba676ed25c9 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -190,7 +190,8 @@ 'CA': 'Certificate', 'listElastistorInterface': 'Misc', 'cloudian': 'Cloudian', - 'Sioc' : 'Sioc' + 'Sioc' : 'Sioc', + 'retrieveDiagnostics': 'System VM' } diff --git a/ui/l10n/ar.js b/ui/l10n/ar.js index 87c119306be5..398f24588685 100644 --- a/ui/l10n/ar.js +++ b/ui/l10n/ar.js @@ -64,6 +64,7 @@ var dictionary = { "label.IKE.policy": "سياسة IKE", "label.IPsec.preshared.key": "مفتاح أمن بروتوكول الإنترنت تمت مشاركته مسبقا", "label.LB.isolation": "LB isolation", + "label.retrievediagnostics.diagnostics": "Retrieve diagnostics", "label.LUN.number": "LUN #", "label.PA": "Palo Alto", "label.PA.log.profile": "Palo Alto Log Profile", @@ -632,6 +633,9 @@ var dictionary = { "label.deny": "Deny", "label.deployment.planner": "Deployment planner", "label.description": "Description", + "label.systemvm.name": "System Vm Type", + "label.retrievediagnostics.detail": "List of Files", + "label.diagnosticstype": "Diagnostics Type", "label.destination.physical.network.id": "Destination physical network ID", "label.destination.zone": "Destination Zone", "label.destroy": "هدم", diff --git a/ui/scripts/retrievediagnostics.js b/ui/scripts/retrievediagnostics.js new file mode 100644 index 000000000000..1bce8e5f64dc --- /dev/null +++ b/ui/scripts/retrievediagnostics.js @@ -0,0 +1,164 @@ +// 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. +(function(cloudStack) { + cloudStack.sections.system = { + title: 'label.retrievediagnostics.diagnostics', + listView: { + id: 'retrieveDiagnostics', + fields: { + name: { + label: 'label.name' + }, + type: { + label: 'label.type' + } + }, + + var selectedZoneObj = args.context.physicalResources[0]; + $.ajax({ + url: createURL("listSystemVms&zoneid=" + selectedZoneObj.id ), + dataType: "json" + async: true + success: function(json) { + var items = json.listsystemvmsresponse.systemvm; + args.response.success({ + data: items + }); + } + }); + }, + actions: { + add: { + label: 'label.retrievediagnostics.diagnostics', + + messages: { + notification: function(args) { + return 'label.retrievediagnostics.diagnostics'; + } + }, + + createForm: { + title: 'label.retrievediagnostics.diagnostics', + button: cloudStack.button, + label: "label.retrievediagnostics.diagnostics" + fields: { + systemvmtype: { + label: 'label.systemvm.name', + converter: function (args) { + if (args == "consoleproxy") + return "Console Proxy VM"; else if (args == "secondarystoragevm") + return "Secondary Storage VM"; else + return args; + } + }, + diagnosticstype: { + label: 'label.diagnosticstype', + select: function(args) { + $.ajax({ + success: function(json) { + var types = []; + var items = ["LOGFILES", "IPTABLES", "CONFIGURATIONFILES", "IFCONFIG", "PROPERTYFILES", "DHCPFILES", "USERDATA", "LB", "VPN", "IPTABLESretrieve", "IFCONFIGretrieve", "ROUTEretrieve"]; + if (items != null) { + for (var i = 0; i < items.length; i++) { + types.push({ + id: items[i].type, + //description: items[i].type + }); + } + } + args.response.success({ + data: types + }) + } + }); + } + }, + detail: { + label: 'label.retrievediagnostics.detail', + validation: { + required: false + } + }, + } + }, + + action: function(args) { + var data = { + systemvmtype: args.data.systemvmtype, + diagnosticstype: args.data.diagnosticstype, + detail: args.data.detail + }; + $.ajax({ + var array1 =[]; + array1.push("&systemVmId=" + systemvmtype); + array1.push("&type=" + diagnosticstype); + array1.push("&detail=" + detail); + url: createURL('retrieveDiagnostics' + array1.join("")), + dataType: "json", + async: true, + success: function (json) { + args.response.success({ + data: { + } + }); + }, + error: function (json) { + args.response.error(parseXMLHttpResponse(json)); + } + + }); + }, + + notification: { + poll: pollAsyncJobResult + } + } + }, + detailView: { + actions: { + + viewAll: { + path: 'instances', + label: 'label.instances' + }, + + tabs: { + details: { + title: 'label.details', + fields: [{ + systemvmtype: { + label: 'label.systemvm.name' + } + }, { + diagnosticstype: { + label: 'label.diagnosticstype' + }, + detail: { + label: 'label.retrievediagnostics.detail' + }, + }], + + } + } + } + } + }; + +})(cloudStack); + + +