Skip to content

Commit 50869fe

Browse files
committed
Merge branch '4.12'
2 parents 3e4c55c + bea627a commit 50869fe

9 files changed

Lines changed: 562 additions & 130 deletions

File tree

agent/src/main/java/com/cloud/agent/Agent.java

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939

4040
import javax.naming.ConfigurationException;
4141

42-
import org.apache.cloudstack.agent.directdownload.SetupDirectDownloadCertificate;
4342
import org.apache.cloudstack.agent.lb.SetupMSListAnswer;
4443
import org.apache.cloudstack.agent.lb.SetupMSListCommand;
4544
import org.apache.cloudstack.ca.PostCertificateRenewalCommand;
@@ -630,8 +629,6 @@ protected void processRequest(final Request request, final Link link) {
630629
if (Host.Type.Routing.equals(_resource.getType())) {
631630
scheduleServicesRestartTask();
632631
}
633-
} else if (cmd instanceof SetupDirectDownloadCertificate) {
634-
answer = setupDirectDownloadCertificate((SetupDirectDownloadCertificate) cmd);
635632
} else if (cmd instanceof SetupMSListCommand) {
636633
answer = setupManagementServerList((SetupMSListCommand) cmd);
637634
} else {
@@ -683,31 +680,6 @@ protected void processRequest(final Request request, final Link link) {
683680
}
684681
}
685682

686-
private Answer setupDirectDownloadCertificate(SetupDirectDownloadCertificate cmd) {
687-
String certificate = cmd.getCertificate();
688-
String certificateName = cmd.getCertificateName();
689-
s_logger.info("Importing certificate " + certificateName + " into keystore");
690-
691-
final File agentFile = PropertiesUtil.findConfigFile("agent.properties");
692-
if (agentFile == null) {
693-
return new Answer(cmd, false, "Failed to find agent.properties file");
694-
}
695-
696-
final String keyStoreFile = agentFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME;
697-
698-
String cerFile = agentFile.getParent() + "/" + certificateName + ".cer";
699-
Script.runSimpleBashScript(String.format("echo '%s' > %s", certificate, cerFile));
700-
701-
String privatePasswordFormat = "sed -n '/keystore.passphrase/p' '%s' 2>/dev/null | sed 's/keystore.passphrase=//g' 2>/dev/null";
702-
String privatePasswordCmd = String.format(privatePasswordFormat, agentFile.getAbsolutePath());
703-
String privatePassword = Script.runSimpleBashScript(privatePasswordCmd);
704-
705-
String importCommandFormat = "keytool -importcert -file %s -keystore %s -alias '%s' -storepass '%s' -noprompt";
706-
String importCmd = String.format(importCommandFormat, cerFile, keyStoreFile, certificateName, privatePassword);
707-
Script.runSimpleBashScript(importCmd);
708-
return new Answer(cmd, true, "Certificate " + certificateName + " imported");
709-
}
710-
711683
public Answer setupAgentKeystore(final SetupKeyStoreCommand cmd) {
712684
final String keyStorePassword = cmd.getKeystorePassword();
713685
final long validityDays = cmd.getValidityDays();

api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificate.java renamed to api/src/main/java/org/apache/cloudstack/api/command/admin/direct/download/UploadTemplateDirectDownloadCertificateCmd.java

Lines changed: 92 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,92 @@
1-
// Licensed to the Apache Software Foundation (ASF) under one
2-
// or more contributor license agreements. See the NOTICE file
3-
// distributed with this work for additional information
4-
// regarding copyright ownership. The ASF licenses this file
5-
// to you under the Apache License, Version 2.0 (the
6-
// "License"); you may not use this file except in compliance
7-
// with the License. You may obtain a copy of the License at
8-
//
9-
// http://www.apache.org/licenses/LICENSE-2.0
10-
//
11-
// Unless required by applicable law or agreed to in writing,
12-
// software distributed under the License is distributed on an
13-
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14-
// KIND, either express or implied. See the License for the
15-
// specific language governing permissions and limitations
16-
// under the License.
17-
package org.apache.cloudstack.api.command.admin.direct.download;
18-
19-
import com.cloud.exception.ConcurrentOperationException;
20-
import com.cloud.exception.InsufficientCapacityException;
21-
import com.cloud.exception.ResourceAllocationException;
22-
import com.cloud.exception.ResourceUnavailableException;
23-
import com.cloud.exception.NetworkRuleConflictException;
24-
import org.apache.cloudstack.acl.RoleType;
25-
import org.apache.cloudstack.api.APICommand;
26-
import org.apache.cloudstack.api.ApiConstants;
27-
import org.apache.cloudstack.api.BaseCmd;
28-
import org.apache.cloudstack.api.Parameter;
29-
import org.apache.cloudstack.api.ServerApiException;
30-
import org.apache.cloudstack.api.ApiErrorCode;
31-
import org.apache.cloudstack.api.response.SuccessResponse;
32-
import org.apache.cloudstack.context.CallContext;
33-
import org.apache.cloudstack.direct.download.DirectDownloadManager;
34-
import org.apache.log4j.Logger;
35-
36-
import javax.inject.Inject;
37-
38-
@APICommand(name = UploadTemplateDirectDownloadCertificate.APINAME,
39-
description = "Upload a certificate for HTTPS direct template download on KVM hosts",
40-
responseObject = SuccessResponse.class,
41-
requestHasSensitiveInfo = true,
42-
responseHasSensitiveInfo = true,
43-
since = "4.11.0",
44-
authorized = {RoleType.Admin})
45-
public class UploadTemplateDirectDownloadCertificate extends BaseCmd {
46-
47-
@Inject
48-
DirectDownloadManager directDownloadManager;
49-
50-
private static final Logger LOG = Logger.getLogger(UploadTemplateDirectDownloadCertificate.class);
51-
public static final String APINAME = "uploadTemplateDirectDownloadCertificate";
52-
53-
@Parameter(name = ApiConstants.CERTIFICATE, type = BaseCmd.CommandType.STRING, required = true, length = 65535,
54-
description = "SSL certificate")
55-
private String certificate;
56-
57-
@Parameter(name = ApiConstants.NAME , type = BaseCmd.CommandType.STRING, required = true,
58-
description = "Name for the uploaded certificate")
59-
private String name;
60-
61-
@Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING, required = true, description = "Hypervisor type")
62-
private String hypervisor;
63-
64-
@Override
65-
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
66-
if (!hypervisor.equalsIgnoreCase("kvm")) {
67-
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only");
68-
}
69-
70-
SuccessResponse response = new SuccessResponse(getCommandName());
71-
try {
72-
LOG.debug("Uploading certificate " + name + " to agents for Direct Download");
73-
boolean result = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor);
74-
response.setSuccess(result);
75-
setResponseObject(response);
76-
} catch (Exception e) {
77-
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
78-
}
79-
}
80-
81-
@Override
82-
public String getCommandName() {
83-
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
84-
}
85-
86-
@Override
87-
public long getEntityOwnerId() {
88-
return CallContext.current().getCallingAccount().getId();
89-
}
90-
}
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.direct.download;
18+
19+
import com.cloud.exception.ConcurrentOperationException;
20+
import com.cloud.exception.InsufficientCapacityException;
21+
import com.cloud.exception.ResourceAllocationException;
22+
import com.cloud.exception.ResourceUnavailableException;
23+
import com.cloud.exception.NetworkRuleConflictException;
24+
import org.apache.cloudstack.acl.RoleType;
25+
import org.apache.cloudstack.api.APICommand;
26+
import org.apache.cloudstack.api.ApiConstants;
27+
import org.apache.cloudstack.api.BaseCmd;
28+
import org.apache.cloudstack.api.Parameter;
29+
import org.apache.cloudstack.api.ServerApiException;
30+
import org.apache.cloudstack.api.ApiErrorCode;
31+
import org.apache.cloudstack.api.response.SuccessResponse;
32+
import org.apache.cloudstack.context.CallContext;
33+
import org.apache.cloudstack.direct.download.DirectDownloadManager;
34+
import org.apache.log4j.Logger;
35+
36+
import javax.inject.Inject;
37+
38+
@APICommand(name = UploadTemplateDirectDownloadCertificateCmd.APINAME,
39+
description = "Upload a certificate for HTTPS direct template download on KVM hosts",
40+
responseObject = SuccessResponse.class,
41+
requestHasSensitiveInfo = true,
42+
responseHasSensitiveInfo = true,
43+
since = "4.11.0",
44+
authorized = {RoleType.Admin})
45+
public class UploadTemplateDirectDownloadCertificateCmd extends BaseCmd {
46+
47+
@Inject
48+
DirectDownloadManager directDownloadManager;
49+
50+
private static final Logger LOG = Logger.getLogger(UploadTemplateDirectDownloadCertificateCmd.class);
51+
public static final String APINAME = "uploadTemplateDirectDownloadCertificate";
52+
53+
@Parameter(name = ApiConstants.CERTIFICATE, type = BaseCmd.CommandType.STRING, required = true, length = 65535,
54+
description = "SSL certificate")
55+
private String certificate;
56+
57+
@Parameter(name = ApiConstants.NAME , type = BaseCmd.CommandType.STRING, required = true,
58+
description = "Name for the uploaded certificate")
59+
private String name;
60+
61+
@Parameter(name = ApiConstants.HYPERVISOR, type = BaseCmd.CommandType.STRING, required = true, description = "Hypervisor type")
62+
private String hypervisor;
63+
64+
@Override
65+
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
66+
if (!hypervisor.equalsIgnoreCase("kvm")) {
67+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Currently supporting KVM hosts only");
68+
}
69+
70+
SuccessResponse response = new SuccessResponse(getCommandName());
71+
try {
72+
LOG.debug("Uploading certificate " + name + " to agents for Direct Download");
73+
boolean result = directDownloadManager.uploadCertificateToHosts(certificate, name, hypervisor);
74+
response.setSuccess(result);
75+
setResponseObject(response);
76+
} catch (Exception e) {
77+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
78+
}
79+
}
80+
81+
@Override
82+
public String getCommandName() {
83+
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
84+
}
85+
86+
@Override
87+
public long getEntityOwnerId() {
88+
return CallContext.current().getCallingAccount().getId();
89+
}
90+
}
91+
92+

core/src/main/java/org/apache/cloudstack/agent/directdownload/SetupDirectDownloadCertificate.java renamed to core/src/main/java/org/apache/cloudstack/agent/directdownload/SetupDirectDownloadCertificateCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020

2121
import com.cloud.agent.api.Command;
2222

23-
public class SetupDirectDownloadCertificate extends Command {
23+
public class SetupDirectDownloadCertificateCommand extends Command {
2424

2525
private String certificate;
2626
private String certificateName;
2727

28-
public SetupDirectDownloadCertificate(String certificate, String name) {
28+
public SetupDirectDownloadCertificateCommand(String certificate, String name) {
2929
this.certificate = certificate;
3030
this.certificateName = name;
3131
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
package com.cloud.hypervisor.kvm.resource.wrapper;
20+
21+
import com.cloud.agent.api.Answer;
22+
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
23+
import com.cloud.resource.CommandWrapper;
24+
import com.cloud.resource.ResourceWrapper;
25+
import com.cloud.utils.PropertiesUtil;
26+
import com.cloud.utils.exception.CloudRuntimeException;
27+
import com.cloud.utils.script.Script;
28+
import org.apache.cloudstack.agent.directdownload.SetupDirectDownloadCertificateCommand;
29+
import org.apache.cloudstack.utils.security.KeyStoreUtils;
30+
import org.apache.log4j.Logger;
31+
32+
import java.io.File;
33+
import java.io.FileNotFoundException;
34+
import java.io.IOException;
35+
36+
import static org.apache.commons.lang.StringUtils.isBlank;
37+
38+
@ResourceWrapper(handles = SetupDirectDownloadCertificateCommand.class)
39+
public class LibvirtSetupDirectDownloadCertificateCommandWrapper extends CommandWrapper<SetupDirectDownloadCertificateCommand, Answer, LibvirtComputingResource> {
40+
41+
private static final String temporaryCertFilePrefix = "CSCERTIFICATE";
42+
43+
private static final Logger s_logger = Logger.getLogger(LibvirtSetupDirectDownloadCertificateCommandWrapper.class);
44+
45+
/**
46+
* Retrieve agent.properties file
47+
*/
48+
private File getAgentPropertiesFile() throws FileNotFoundException {
49+
final File agentFile = PropertiesUtil.findConfigFile("agent.properties");
50+
if (agentFile == null) {
51+
throw new FileNotFoundException("Failed to find agent.properties file");
52+
}
53+
return agentFile;
54+
}
55+
56+
/**
57+
* Get the property 'keystore.passphrase' value from agent.properties file
58+
*/
59+
private String getKeystorePassword(File agentFile) {
60+
String pass = null;
61+
if (agentFile != null) {
62+
try {
63+
pass = PropertiesUtil.loadFromFile(agentFile).getProperty(KeyStoreUtils.KS_PASSPHRASE_PROPERTY);
64+
} catch (IOException e) {
65+
s_logger.error("Could not get 'keystore.passphrase' property value due to: " + e.getMessage());
66+
}
67+
}
68+
return pass;
69+
}
70+
71+
/**
72+
* Get keystore path
73+
*/
74+
private String getKeyStoreFilePath(File agentFile) {
75+
return agentFile.getParent() + "/" + KeyStoreUtils.KS_FILENAME;
76+
}
77+
78+
/**
79+
* Import certificate from temporary file into keystore
80+
*/
81+
private void importCertificate(String tempCerFilePath, String keyStoreFile, String certificateName, String privatePassword) {
82+
s_logger.debug("Importing certificate from temporary file to keystore");
83+
String importCommandFormat = "keytool -importcert -file %s -keystore %s -alias '%s' -storepass '%s' -noprompt";
84+
String importCmd = String.format(importCommandFormat, tempCerFilePath, keyStoreFile, certificateName, privatePassword);
85+
int result = Script.runSimpleBashScriptForExitValue(importCmd);
86+
if (result != 0) {
87+
s_logger.debug("Certificate " + certificateName + " not imported as it already exist on keystore");
88+
}
89+
}
90+
91+
/**
92+
* Create temporary file and return its path
93+
*/
94+
private String createTemporaryFile(File agentFile, String certificateName, String certificate) {
95+
String tempCerFilePath = String.format("%s/%s-%s",
96+
agentFile.getParent(), temporaryCertFilePrefix, certificateName);
97+
s_logger.debug("Creating temporary certificate file into: " + tempCerFilePath);
98+
int result = Script.runSimpleBashScriptForExitValue(String.format("echo '%s' > %s", certificate, tempCerFilePath));
99+
if (result != 0) {
100+
throw new CloudRuntimeException("Could not create the certificate file on path: " + tempCerFilePath);
101+
}
102+
return tempCerFilePath;
103+
}
104+
105+
/**
106+
* Remove temporary file
107+
*/
108+
private void cleanupTemporaryFile(String temporaryFile) {
109+
s_logger.debug("Cleaning up temporary certificate file");
110+
Script.runSimpleBashScript("rm -f " + temporaryFile);
111+
}
112+
113+
@Override
114+
public Answer execute(SetupDirectDownloadCertificateCommand cmd, LibvirtComputingResource serverResource) {
115+
String certificate = cmd.getCertificate();
116+
String certificateName = cmd.getCertificateName();
117+
118+
try {
119+
File agentFile = getAgentPropertiesFile();
120+
String privatePassword = getKeystorePassword(agentFile);
121+
if (isBlank(privatePassword)) {
122+
return new Answer(cmd, false, "No password found for keystore: " + KeyStoreUtils.KS_FILENAME);
123+
}
124+
125+
final String keyStoreFile = getKeyStoreFilePath(agentFile);
126+
String temporaryFile = createTemporaryFile(agentFile, certificateName, certificate);
127+
importCertificate(temporaryFile, keyStoreFile, certificateName, privatePassword);
128+
cleanupTemporaryFile(temporaryFile);
129+
} catch (FileNotFoundException | CloudRuntimeException e) {
130+
s_logger.error("Error while setting up certificate " + certificateName, e);
131+
return new Answer(cmd, false, e.getMessage());
132+
}
133+
134+
return new Answer(cmd, true, "Certificate " + certificateName + " imported");
135+
}
136+
}

0 commit comments

Comments
 (0)