mirror of https://github.com/apache/cloudstack.git
Add support for using different CNI plugins with CKS
* Add support for using different CNI plugins with CKS * remove unused import * Add UI support and list cni config API * necessary UI changes * add license * changes to support external cni * UI changes * Fix NPE on restarting VPC with additional public IPs * fix merge conflict * add asnumber to create k8s svc layer * support cni framework to use as-numbers * update code * condition to ignore undefined jinja template variables
This commit is contained in:
parent
b0eb65f31a
commit
2a1de76517
|
|
@ -289,6 +289,7 @@ public class EventTypes {
|
|||
|
||||
//registering userdata events
|
||||
public static final String EVENT_REGISTER_USER_DATA = "REGISTER.USER.DATA";
|
||||
public static final String EVENT_REGISTER_CNI_CONFIG = "REGISTER.CNI.CONFIG";
|
||||
|
||||
//register for user API and secret keys
|
||||
public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY";
|
||||
|
|
|
|||
|
|
@ -164,4 +164,6 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm
|
|||
Long getWorkerTemplateId();
|
||||
Long getEtcdTemplateId();
|
||||
Long getEtcdNodeCount();
|
||||
Long getCniConfigId();
|
||||
String getCniConfigDetails();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ import org.apache.cloudstack.api.command.user.ssh.CreateSSHKeyPairCmd;
|
|||
import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd;
|
||||
import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd;
|
||||
import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.BaseRegisterUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
|
||||
import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
|
|
@ -360,17 +360,16 @@ public interface ManagementService {
|
|||
* The api command class.
|
||||
* @return The list of userdatas found.
|
||||
*/
|
||||
Pair<List<? extends UserData>, Integer> listUserDatas(ListUserDataCmd cmd);
|
||||
Pair<List<? extends UserData>, Integer> listUserDatas(ListUserDataCmd cmd, boolean forCks);
|
||||
|
||||
/**
|
||||
* Registers a userdata.
|
||||
*
|
||||
* @param cmd
|
||||
* The api command class.
|
||||
* @param cmd The api command class.
|
||||
* @param forCks
|
||||
* @return A VO with the registered userdata.
|
||||
*/
|
||||
UserData registerUserData(RegisterUserDataCmd cmd);
|
||||
|
||||
UserData registerUserData(BaseRegisterUserDataCmd cmd);
|
||||
/**
|
||||
* Deletes a userdata.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -29,4 +29,5 @@ public interface UserData extends ControlledEntity, InternalIdentity, Identity {
|
|||
String getUserData();
|
||||
|
||||
String getParams();
|
||||
boolean isForCks();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ public class ApiConstants {
|
|||
public static final String CN = "cn";
|
||||
public static final String COMMAND = "command";
|
||||
public static final String CMD_EVENT_TYPE = "cmdeventtype";
|
||||
public static final String CNI_CONFIG = "cniconfig";
|
||||
public static final String CNI_CONFIG_ID = "cniconfigurationid";
|
||||
public static final String CNI_CONFIG_DETAILS = "cniconfigdetails";
|
||||
public static final String COMPONENT = "component";
|
||||
public static final String CPU_CORE_PER_SOCKET = "cpucorepersocket";
|
||||
public static final String CPU_NUMBER = "cpunumber";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
// 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.user.userdata;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseRegisterUserDataCmd extends BaseCmd {
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the userdata")
|
||||
private String name;
|
||||
|
||||
//Owner information
|
||||
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the userdata. Must be used with domainId.")
|
||||
private String accountName;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = DomainResponse.class,
|
||||
description = "an optional domainId for the userdata. If the account parameter is used, domainId must also be used.")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the userdata")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in userdata content")
|
||||
private String params;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
checkForVRMetadataFileNames(params);
|
||||
return params;
|
||||
}
|
||||
|
||||
public void checkForVRMetadataFileNames(String params) {
|
||||
if (StringUtils.isNotEmpty(params)) {
|
||||
List<String> keyValuePairs = new ArrayList<>(Arrays.asList(params.split(",")));
|
||||
keyValuePairs.retainAll(NetworkModel.metadataFileNames);
|
||||
if (!keyValuePairs.isEmpty()) {
|
||||
throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.userdata;
|
||||
|
||||
import com.cloud.user.UserData;
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = "listCniConfiguration", description = "List userdata for CNI plugins", responseObject = UserDataResponse.class, entityType = {UserData.class},
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.20",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class ListCniConfigurationCmd extends ListUserDataCmd {
|
||||
public static final Logger s_logger = Logger.getLogger(ListCniConfigurationCmd.class.getName());
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
Pair<List<? extends UserData>, Integer> resultList = _mgr.listUserDatas(this, true);
|
||||
List<UserDataResponse> responses = new ArrayList<>();
|
||||
for (UserData result : resultList.first()) {
|
||||
UserDataResponse r = _responseGenerator.createUserDataResponse(result);
|
||||
r.setObjectName(ApiConstants.CNI_CONFIG);
|
||||
responses.add(r);
|
||||
}
|
||||
|
||||
ListResponse<UserDataResponse> response = new ListResponse<>();
|
||||
response.setResponses(responses, resultList.second());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ public class ListUserDataCmd extends BaseListProjectAndAccountResourcesCmd {
|
|||
|
||||
@Override
|
||||
public void execute() {
|
||||
Pair<List<? extends UserData>, Integer> resultList = _mgr.listUserDatas(this);
|
||||
Pair<List<? extends UserData>, Integer> resultList = _mgr.listUserDatas(this, false);
|
||||
List<UserDataResponse> responses = new ArrayList<>();
|
||||
for (UserData result : resultList.first()) {
|
||||
UserDataResponse r = _responseGenerator.createUserDataResponse(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
// 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.user.userdata;
|
||||
|
||||
import com.cloud.user.UserData;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@APICommand(name = "registerCniConfiguration",
|
||||
description = "Register a CNI Configuration to be used with CKS cluster",
|
||||
since = "4.19.0",
|
||||
responseObject = SuccessResponse.class,
|
||||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class RegisterCniConfigurationCmd extends BaseRegisterUserDataCmd {
|
||||
public static final Logger s_logger = Logger.getLogger(RegisterCniConfigurationCmd.class.getName());
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.CNI_CONFIG, type = CommandType.STRING, description = "CNI Configuration content to be registered as User data", length = 1048576)
|
||||
private String cniConfig;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public String getCniConfig() {
|
||||
return cniConfig;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
UserData result = _mgr.registerUserData(this);
|
||||
UserDataResponse response = _responseGenerator.createUserDataResponse(result);
|
||||
response.setResponseName(getCommandName());
|
||||
response.setObjectName(ApiConstants.CNI_CONFIG);
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true);
|
||||
if (accountId == null) {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
||||
return accountId;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,30 +16,20 @@
|
|||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.userdata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.NetworkRuleConflictException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.user.UserData;
|
||||
|
||||
@APICommand(name = "registerUserData",
|
||||
|
|
@ -49,89 +39,28 @@ import com.cloud.user.UserData;
|
|||
requestHasSensitiveInfo = false,
|
||||
responseHasSensitiveInfo = false,
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class RegisterUserDataCmd extends BaseCmd {
|
||||
public class RegisterUserDataCmd extends BaseRegisterUserDataCmd {
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the userdata")
|
||||
private String name;
|
||||
@Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, required = true, description = "Userdata content", length = 1048576)
|
||||
protected String userData;
|
||||
|
||||
//Owner information
|
||||
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the userdata. Must be used with domainId.")
|
||||
private String accountName;
|
||||
|
||||
@Parameter(name = ApiConstants.DOMAIN_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = DomainResponse.class,
|
||||
description = "an optional domainId for the userdata. If the account parameter is used, domainId must also be used.")
|
||||
private Long domainId;
|
||||
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the userdata")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.USER_DATA,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "Base64 encoded userdata content. " +
|
||||
"Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " +
|
||||
"Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " +
|
||||
"You also need to change vm.userdata.max.length value",
|
||||
length = 1048576)
|
||||
private String userData;
|
||||
|
||||
@Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in userdata content")
|
||||
private String params;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public Long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public String getUserData() {
|
||||
return userData;
|
||||
}
|
||||
|
||||
public String getParams() {
|
||||
checkForVRMetadataFileNames(params);
|
||||
return params;
|
||||
}
|
||||
|
||||
public void checkForVRMetadataFileNames(String params) {
|
||||
if (StringUtils.isNotEmpty(params)) {
|
||||
List<String> keyValuePairs = new ArrayList<>(Arrays.asList(params.split(",")));
|
||||
keyValuePairs.retainAll(NetworkModel.metadataFileNames);
|
||||
if (!keyValuePairs.isEmpty()) {
|
||||
throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true);
|
||||
Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true);
|
||||
if (accountId == null) {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class ListUserDataCmdTest {
|
|||
Pair<List<? extends UserData>, Integer> result = new Pair<List<? extends UserData>, Integer>(userDataList, 1);
|
||||
UserDataResponse userDataResponse = Mockito.mock(UserDataResponse.class);
|
||||
|
||||
Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result);
|
||||
Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result);
|
||||
Mockito.when(_responseGenerator.createUserDataResponse(userData)).thenReturn(userDataResponse);
|
||||
|
||||
cmd.execute();
|
||||
|
|
@ -82,7 +82,7 @@ public class ListUserDataCmdTest {
|
|||
List<UserData> userDataList = new ArrayList<UserData>();
|
||||
Pair<List<? extends UserData>, Integer> result = new Pair<List<? extends UserData>, Integer>(userDataList, 0);
|
||||
|
||||
Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result);
|
||||
Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result);
|
||||
|
||||
cmd.execute();
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,6 @@ import java.sql.Connection;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.upgrade.SystemVmTemplateRegistration;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
|
@ -61,7 +57,6 @@ public class Upgrade41910to42000 extends DbUpgradeAbstractImpl implements DbUpgr
|
|||
|
||||
@Override
|
||||
public void performDataMigration(Connection conn) {
|
||||
updateKubernetesClusterNodeVersions(conn);
|
||||
checkAndUpdateAffinityGroupNameCharSetToUtf8mb4(conn);
|
||||
}
|
||||
|
||||
|
|
@ -91,95 +86,6 @@ public class Upgrade41910to42000 extends DbUpgradeAbstractImpl implements DbUpgr
|
|||
}
|
||||
}
|
||||
|
||||
private Map<Long, String> getKubernetesClusterIdsAndVersion(Connection conn) {
|
||||
String listKubernetesClusters = "SELECT c.id, v.semantic_version FROM `cloud`.`kubernetes_cluster` c JOIN `cloud`.`kubernetes_supported_version` v ON (c.kubernetes_version_id = v.id) WHERE c.removed is NULL;";
|
||||
Map<Long, String> clusterAndVersion = new HashMap<>();
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(listKubernetesClusters);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
clusterAndVersion.put(rs.getLong(1), rs.getString(2));
|
||||
}
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
String errMsg = String.format("Failed to get all the kubernetes cluster ids due to: %s", e.getMessage());
|
||||
logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
return clusterAndVersion;
|
||||
}
|
||||
|
||||
private List<Long> getKubernetesClusterVmMapIds(Connection conn, Long cksClusterId) {
|
||||
List<Long> kubernetesClusterVmIds = new ArrayList<>();
|
||||
String getKubernetesClustersVmMap = "SELECT id FROM `cloud`.`kubernetes_cluster_vm_map` WHERE cluster_id = %s;";
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(String.format(getKubernetesClustersVmMap, cksClusterId));
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
kubernetesClusterVmIds.add(rs.getLong(1));
|
||||
}
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
String errMsg = String.format("Failed to get the kubernetes cluster vm map IDs for kubernetes cluster with id: %s," +
|
||||
" due to: %s", cksClusterId, e.getMessage());
|
||||
logger.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
return kubernetesClusterVmIds;
|
||||
}
|
||||
|
||||
private void updateKubernetesNodeVersion(Connection conn, List<Long> kubernetesClusterVmIds, Long cksClusterId, String cksVersion) {
|
||||
String updateKubernetesNodeVersion = "UPDATE `cloud`.`kubernetes_cluster_vm_map` set kubernetes_node_version = ? WHERE id = ?;";
|
||||
for (Long nodeVmId : kubernetesClusterVmIds) {
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(updateKubernetesNodeVersion);
|
||||
pstmt.setString(1, cksVersion);
|
||||
pstmt.setLong(2, nodeVmId);
|
||||
pstmt.executeUpdate();
|
||||
pstmt.close();
|
||||
} catch (Exception e) {
|
||||
String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" +
|
||||
" kubernetes cluster with id: %s," +
|
||||
" due to: %s", cksClusterId, e.getMessage());
|
||||
logger.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateKubernetesNodeVersions(Connection conn, Map<Long, String> clusterAndVersion) {
|
||||
List<Long> kubernetesClusterVmIds;
|
||||
for (Map.Entry<Long, String> clusterVersionEntry : clusterAndVersion.entrySet()) {
|
||||
try {
|
||||
Long cksClusterId = clusterVersionEntry.getKey();
|
||||
String cksVersion = clusterVersionEntry.getValue();
|
||||
logger.debug(String.format("Adding CKS version %s to existing CKS cluster %s nodes", cksVersion, cksClusterId));
|
||||
kubernetesClusterVmIds = getKubernetesClusterVmMapIds(conn, cksClusterId);
|
||||
updateKubernetesNodeVersion(conn, kubernetesClusterVmIds, cksClusterId, cksVersion);
|
||||
} catch (Exception e) {
|
||||
String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" +
|
||||
" kubernetes cluster with id: %s," +
|
||||
" due to: %s", clusterVersionEntry.getKey(), e.getMessage());
|
||||
logger.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateKubernetesClusterNodeVersions(Connection conn) {
|
||||
//get list of all non removed kubernetes clusters
|
||||
try {
|
||||
Map<Long, String> clusterAndVersion = getKubernetesClusterIdsAndVersion(conn);
|
||||
updateKubernetesNodeVersions(conn, clusterAndVersion);
|
||||
} catch (Exception e) {
|
||||
String errMsg = "Failed to update kubernetes cluster nodes version";
|
||||
logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndUpdateAffinityGroupNameCharSetToUtf8mb4(Connection conn) {
|
||||
logger.debug("Check and update char set for affinity group name to utf8mb4");
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate {
|
||||
private SystemVmTemplateRegistration systemVmTemplateRegistration;
|
||||
|
|
@ -53,6 +60,7 @@ public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgr
|
|||
|
||||
@Override
|
||||
public void performDataMigration(Connection conn) {
|
||||
updateKubernetesClusterNodeVersions(conn);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -80,4 +88,93 @@ public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgr
|
|||
throw new CloudRuntimeException("Failed to find / register SystemVM template(s)");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateKubernetesClusterNodeVersions(Connection conn) {
|
||||
//get list of all non removed kubernetes clusters
|
||||
try {
|
||||
Map<Long, String> clusterAndVersion = getKubernetesClusterIdsAndVersion(conn);
|
||||
updateKubernetesNodeVersions(conn, clusterAndVersion);
|
||||
} catch (Exception e) {
|
||||
String errMsg = "Failed to update kubernetes cluster nodes version";
|
||||
logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Long, String> getKubernetesClusterIdsAndVersion(Connection conn) {
|
||||
String listKubernetesClusters = "SELECT c.id, v.semantic_version FROM `cloud`.`kubernetes_cluster` c JOIN `cloud`.`kubernetes_supported_version` v ON (c.kubernetes_version_id = v.id) WHERE c.removed is NULL;";
|
||||
Map<Long, String> clusterAndVersion = new HashMap<>();
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(listKubernetesClusters);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
clusterAndVersion.put(rs.getLong(1), rs.getString(2));
|
||||
}
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
String errMsg = String.format("Failed to get all the kubernetes cluster ids due to: %s", e.getMessage());
|
||||
logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
return clusterAndVersion;
|
||||
}
|
||||
|
||||
private void updateKubernetesNodeVersions(Connection conn, Map<Long, String> clusterAndVersion) {
|
||||
List<Long> kubernetesClusterVmIds;
|
||||
for (Map.Entry<Long, String> clusterVersionEntry : clusterAndVersion.entrySet()) {
|
||||
try {
|
||||
Long cksClusterId = clusterVersionEntry.getKey();
|
||||
String cksVersion = clusterVersionEntry.getValue();
|
||||
logger.debug(String.format("Adding CKS version %s to existing CKS cluster %s nodes", cksVersion, cksClusterId));
|
||||
kubernetesClusterVmIds = getKubernetesClusterVmMapIds(conn, cksClusterId);
|
||||
updateKubernetesNodeVersion(conn, kubernetesClusterVmIds, cksClusterId, cksVersion);
|
||||
} catch (Exception e) {
|
||||
String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" +
|
||||
" kubernetes cluster with id: %s," +
|
||||
" due to: %s", clusterVersionEntry.getKey(), e.getMessage());
|
||||
logger.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> getKubernetesClusterVmMapIds(Connection conn, Long cksClusterId) {
|
||||
List<Long> kubernetesClusterVmIds = new ArrayList<>();
|
||||
String getKubernetesClustersVmMap = "SELECT id FROM `cloud`.`kubernetes_cluster_vm_map` WHERE cluster_id = %s;";
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(String.format(getKubernetesClustersVmMap, cksClusterId));
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
kubernetesClusterVmIds.add(rs.getLong(1));
|
||||
}
|
||||
rs.close();
|
||||
pstmt.close();
|
||||
} catch (SQLException e) {
|
||||
String errMsg = String.format("Failed to get the kubernetes cluster vm map IDs for kubernetes cluster with id: %s," +
|
||||
" due to: %s", cksClusterId, e.getMessage());
|
||||
logger.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
return kubernetesClusterVmIds;
|
||||
}
|
||||
|
||||
private void updateKubernetesNodeVersion(Connection conn, List<Long> kubernetesClusterVmIds, Long cksClusterId, String cksVersion) {
|
||||
String updateKubernetesNodeVersion = "UPDATE `cloud`.`kubernetes_cluster_vm_map` set kubernetes_node_version = ? WHERE id = ?;";
|
||||
for (Long nodeVmId : kubernetesClusterVmIds) {
|
||||
try {
|
||||
PreparedStatement pstmt = conn.prepareStatement(updateKubernetesNodeVersion);
|
||||
pstmt.setString(1, cksVersion);
|
||||
pstmt.setLong(2, nodeVmId);
|
||||
pstmt.executeUpdate();
|
||||
pstmt.close();
|
||||
} catch (Exception e) {
|
||||
String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" +
|
||||
" kubernetes cluster with id: %s," +
|
||||
" due to: %s", cksClusterId, e.getMessage());
|
||||
logger.error(errMsg, e);
|
||||
throw new CloudRuntimeException(errMsg, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ public class UserDataVO implements UserData {
|
|||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
private Date removed;
|
||||
|
||||
@Column(name = "for_cks")
|
||||
private boolean forCks;
|
||||
|
||||
@Override
|
||||
public long getDomainId() {
|
||||
return domainId;
|
||||
|
|
@ -105,6 +108,11 @@ public class UserDataVO implements UserData {
|
|||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForCks() {
|
||||
return forCks;
|
||||
}
|
||||
|
||||
public void setAccountId(long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
|
@ -132,4 +140,6 @@ public class UserDataVO implements UserData {
|
|||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void setForCks(boolean forCks) { this.forCks = forCks; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -425,26 +425,3 @@ INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid, hypervisor_type, hypervi
|
|||
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_instance', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for vm" ');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for volumes" ');
|
||||
|
||||
-- Add for_cks column to the vm_template table
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the template can be used for CKS cluster deployment"');
|
||||
|
||||
-- Add support for different node types service offerings on CKS clusters
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Control Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Worker Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_service_offering_id', 'bigint unsigned COMMENT "service offering ID for etcd Nodes"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_count', 'bigint unsigned COMMENT "number of etcd nodes to be deployed for the Kubernetes cluster"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_template_id', 'bigint unsigned COMMENT "template id to be used for Control Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_template_id', 'bigint unsigned COMMENT "template id to be used for Worker Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_template_id', 'bigint unsigned COMMENT "template id to be used for etcd Nodes"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','etcd_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the VM is an etcd node"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','external_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node was imported into the Kubernetes cluster"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','manual_upgrade', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node is marked for manual upgrade and excluded from the Kubernetes cluster upgrade operation"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','kubernetes_node_version', 'varchar(40) COMMENT "version of k8s the cluster node is on"');
|
||||
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_service_offering_id` FOREIGN KEY `fk_cluster__control_service_offering_id`(`control_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_service_offering_id` FOREIGN KEY `fk_cluster__worker_service_offering_id`(`worker_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_service_offering_id` FOREIGN KEY `fk_cluster__etcd_service_offering_id`(`etcd_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_template_id` FOREIGN KEY `fk_cluster__control_template_id`(`control_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_template_id` FOREIGN KEY `fk_cluster__worker_template_id`(`worker_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_template_id` FOREIGN KEY `fk_cluster__etcd_template_id`(`etcd_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE;
|
||||
|
|
|
|||
|
|
@ -24,3 +24,37 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'console_endpoint_
|
|||
|
||||
-- Add client_address column to cloud.console_session table
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'client_address', 'VARCHAR(45)');
|
||||
|
||||
-----------------------------------------------------------
|
||||
-- CKS Enhancements:
|
||||
-----------------------------------------------------------
|
||||
-- Add for_cks column to the vm_template table
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the template can be used for CKS cluster deployment"');
|
||||
|
||||
-- Add support for different node types service offerings on CKS clusters
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Control Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Worker Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_service_offering_id', 'bigint unsigned COMMENT "service offering ID for etcd Nodes"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_count', 'bigint unsigned COMMENT "number of etcd nodes to be deployed for the Kubernetes cluster"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_template_id', 'bigint unsigned COMMENT "template id to be used for Control Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_template_id', 'bigint unsigned COMMENT "template id to be used for Worker Node(s)"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_template_id', 'bigint unsigned COMMENT "template id to be used for etcd Nodes"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','cni_config_id', 'bigint unsigned COMMENT "userdata id representing the associated cni configuration"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','cni_config_details', 'varchar(4096) DEFAULT NULL COMMENT "userdata details representing the values required for the cni configuration associated"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','etcd_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the VM is an etcd node"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','external_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node was imported into the Kubernetes cluster"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','manual_upgrade', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node is marked for manual upgrade and excluded from the Kubernetes cluster upgrade operation"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','kubernetes_node_version', 'varchar(40) COMMENT "version of k8s the cluster node is on"');
|
||||
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_service_offering_id` FOREIGN KEY `fk_cluster__control_service_offering_id`(`control_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_service_offering_id` FOREIGN KEY `fk_cluster__worker_service_offering_id`(`worker_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_service_offering_id` FOREIGN KEY `fk_cluster__etcd_service_offering_id`(`etcd_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_template_id` FOREIGN KEY `fk_cluster__control_template_id`(`control_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_template_id` FOREIGN KEY `fk_cluster__worker_template_id`(`worker_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE;
|
||||
ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_template_id` FOREIGN KEY `fk_cluster__etcd_template_id`(`etcd_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE;
|
||||
|
||||
-- Add for_cks column to the user_data table to represent CNI Configuration stored as userdata
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the userdata represent CNI configuration meant for CKS use only"');
|
||||
-----------------------------------------------------------
|
||||
-- END - CKS Enhancements
|
||||
-----------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -47,13 +47,16 @@ import java.util.stream.Collectors;
|
|||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.dc.ASNumberVO;
|
||||
import com.cloud.dc.DedicatedResourceVO;
|
||||
import com.cloud.dc.dao.ASNumberDao;
|
||||
import com.cloud.dc.dao.DedicatedResourceDao;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.kubernetes.cluster.KubernetesServiceHelper.KubernetesClusterNodeType;
|
||||
import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterRemoveWorker;
|
||||
import com.cloud.network.NetworkServiceImpl;
|
||||
import com.cloud.network.dao.NsxProviderDao;
|
||||
import com.cloud.network.element.NsxProviderVO;
|
||||
import com.cloud.kubernetes.cluster.actionworkers.KubernetesClusterAddWorker;
|
||||
|
|
@ -316,6 +319,8 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
private PortForwardingRulesDao pfRuleDao;
|
||||
@Inject
|
||||
RoutedIpv4Manager routedIpv4Manager;
|
||||
@Inject
|
||||
private ASNumberDao asNumberDao;
|
||||
|
||||
private void logMessage(final Level logLevel, final String message, final Exception e) {
|
||||
if (logLevel == Level.WARN) {
|
||||
|
|
@ -1037,7 +1042,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
}
|
||||
|
||||
private Network getKubernetesClusterNetworkIfMissing(final String clusterName, final DataCenter zone, final Account owner, final int controlNodesCount,
|
||||
final int nodesCount, final String externalLoadBalancerIpAddress, final Long networkId) throws CloudRuntimeException {
|
||||
final int nodesCount, final String externalLoadBalancerIpAddress, final Long networkId, final Long asNumber) throws CloudRuntimeException {
|
||||
Network network = null;
|
||||
if (networkId != null) {
|
||||
network = networkDao.findById(networkId);
|
||||
|
|
@ -1070,6 +1075,10 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
network = networkService.createGuestNetwork(networkOffering.getId(), clusterName + "-network",
|
||||
owner.getAccountName() + "-network", owner, physicalNetwork, zone.getId(),
|
||||
ControlledEntity.ACLType.Account);
|
||||
ASNumberVO asNumberVO = NetworkServiceImpl.checkAndSelectASNumber(asNumber, zone, networkOffering, asNumberDao);
|
||||
if (Objects.nonNull(asNumber)) {
|
||||
NetworkServiceImpl.allocateASNumber(asNumberVO, network, asNumberDao);
|
||||
}
|
||||
} catch (ConcurrentOperationException | InsufficientCapacityException | ResourceAllocationException e) {
|
||||
logAndThrow(Level.ERROR, String.format("Unable to create network for the Kubernetes cluster: %s", clusterName));
|
||||
} finally {
|
||||
|
|
@ -1433,6 +1442,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId());
|
||||
final KubernetesSupportedVersion clusterKubernetesVersion = kubernetesSupportedVersionDao.findById(cmd.getKubernetesVersionId());
|
||||
final Hypervisor.HypervisorType hypervisor = cmd.getHypervisorType();
|
||||
final Long asNumber = cmd.getAsNumber();
|
||||
|
||||
Map<String, Long> serviceOfferingNodeTypeMap = cmd.getServiceOfferingNodeTypeMap();
|
||||
Long defaultServiceOfferingId = cmd.getServiceOfferingId();
|
||||
|
|
@ -1457,7 +1467,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
final VMTemplateVO controlNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, CONTROL);
|
||||
final VMTemplateVO workerNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, WORKER);
|
||||
final VMTemplateVO etcdNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, ETCD);
|
||||
final Network defaultNetwork = getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, (int)controlNodeCount, (int)clusterSize, cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId());
|
||||
final Network defaultNetwork = getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, (int)controlNodeCount, (int)clusterSize, cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId(), asNumber);
|
||||
final SecurityGroup finalSecurityGroup = securityGroup;
|
||||
final KubernetesClusterVO cluster = Transaction.execute(new TransactionCallback<KubernetesClusterVO>() {
|
||||
@Override
|
||||
|
|
@ -1472,6 +1482,12 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
defaultNetwork.getId(), owner.getDomainId(), owner.getAccountId(), controlNodeCount, clusterSize,
|
||||
KubernetesCluster.State.Created, cmd.getSSHKeyPairName(), cores, memory,
|
||||
cmd.getNodeRootDiskSize(), "", KubernetesCluster.ClusterType.CloudManaged);
|
||||
newCluster.setCniConfigId(cmd.getCniConfigId());
|
||||
String cniConfigDetails = null;
|
||||
if (MapUtils.isNotEmpty(cmd.getCniConfigDetails())) {
|
||||
cniConfigDetails = cmd.getCniConfigDetails().toString();
|
||||
}
|
||||
newCluster.setCniConfigDetails(cniConfigDetails);
|
||||
if (serviceOfferingNodeTypeMap.containsKey(WORKER.name())) {
|
||||
newCluster.setWorkerServiceOfferingId(serviceOfferingNodeTypeMap.get(WORKER.name()));
|
||||
}
|
||||
|
|
@ -1635,7 +1651,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
|
|||
* @return
|
||||
* @throws CloudRuntimeException
|
||||
*/
|
||||
protected boolean startKubernetesCluster(long kubernetesClusterId, Long domainId, String accountName, Long asNumber, boolean onCreate) throws CloudRuntimeException, ManagementServerException, ResourceUnavailableException, InsufficientCapacityException {
|
||||
@Override
|
||||
public boolean startKubernetesCluster(long kubernetesClusterId, Long domainId, String accountName, Long asNumber, boolean onCreate)
|
||||
throws CloudRuntimeException, ManagementServerException, ResourceUnavailableException, InsufficientCapacityException {
|
||||
if (!KubernetesServiceEnabled.value()) {
|
||||
logAndThrow(Level.ERROR, "Kubernetes Service plugin is disabled");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ public interface KubernetesClusterService extends PluggableService, Configurable
|
|||
|
||||
void startKubernetesCluster(StartKubernetesClusterCmd cmd) throws CloudRuntimeException, ManagementServerException, ResourceUnavailableException, InsufficientCapacityException;
|
||||
|
||||
boolean startKubernetesCluster(long kubernetesClusterId, Long domainId, String accountName, Long asNumber, boolean onCreate) throws CloudRuntimeException, ManagementServerException, ResourceUnavailableException, InsufficientCapacityException;
|
||||
|
||||
boolean stopKubernetesCluster(StopKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
||||
boolean deleteKubernetesCluster(DeleteKubernetesClusterCmd cmd) throws CloudRuntimeException;
|
||||
|
|
|
|||
|
|
@ -138,6 +138,12 @@ public class KubernetesClusterVO implements KubernetesCluster {
|
|||
@Column(name = "etcd_template_id")
|
||||
private Long etcdTemplateId;
|
||||
|
||||
@Column(name = "cni_config_id", nullable = true)
|
||||
private Long cniConfigId = null;
|
||||
|
||||
@Column(name = "cni_config_details", updatable = true, length = 4096)
|
||||
private String cniConfigDetails;
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
|
|
@ -483,4 +489,21 @@ public class KubernetesClusterVO implements KubernetesCluster {
|
|||
public void setControlTemplateId(Long controlTemplateId) {
|
||||
this.controlTemplateId = controlTemplateId;
|
||||
}
|
||||
|
||||
public Long getCniConfigId() {
|
||||
return cniConfigId;
|
||||
}
|
||||
|
||||
public void setCniConfigId(Long cniConfigId) {
|
||||
this.cniConfigId = cniConfigId;
|
||||
}
|
||||
|
||||
public String getCniConfigDetails() {
|
||||
return cniConfigDetails;
|
||||
}
|
||||
|
||||
public void setCniConfigDetails(String cniConfigDetails) {
|
||||
this.cniConfigDetails = cniConfigDetails;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import com.cloud.network.rules.PortForwardingRuleVO;
|
|||
import com.cloud.network.rules.RulesService;
|
||||
import com.cloud.network.rules.dao.PortForwardingRulesDao;
|
||||
import com.cloud.user.SSHKeyPairVO;
|
||||
import com.cloud.user.dao.UserDataDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.db.TransactionCallbackWithException;
|
||||
import com.cloud.utils.net.Ip;
|
||||
|
|
@ -69,6 +70,7 @@ import org.apache.cloudstack.config.ApiServiceConfiguration;
|
|||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.userdata.UserDataManager;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
|
@ -179,6 +181,10 @@ public class KubernetesClusterActionWorker {
|
|||
@Inject
|
||||
protected UserVmService userVmService;
|
||||
@Inject
|
||||
protected UserDataManager userDataManager;
|
||||
@Inject
|
||||
protected UserDataDao userDataDao;
|
||||
@Inject
|
||||
protected UserVmManager userVmManager;
|
||||
@Inject
|
||||
protected VlanDao vlanDao;
|
||||
|
|
@ -725,20 +731,30 @@ public class KubernetesClusterActionWorker {
|
|||
String command = String.format("sudo /opt/bin/kubectl annotate node %s cluster-autoscaler.kubernetes.io/scale-down-disabled=true ; ", name);
|
||||
commands.append(command);
|
||||
}
|
||||
try {
|
||||
File pkFile = getManagementServerSshPublicKeyFile();
|
||||
Pair<String, Integer> publicIpSshPort = getKubernetesClusterServerIpSshPort(null);
|
||||
publicIpAddress = publicIpSshPort.first();
|
||||
sshPort = publicIpSshPort.second();
|
||||
int retryCounter = 0;
|
||||
while (retryCounter < 3) {
|
||||
retryCounter++;
|
||||
try {
|
||||
File pkFile = getManagementServerSshPublicKeyFile();
|
||||
Pair<String, Integer> publicIpSshPort = getKubernetesClusterServerIpSshPort(null);
|
||||
publicIpAddress = publicIpSshPort.first();
|
||||
sshPort = publicIpSshPort.second();
|
||||
|
||||
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
|
||||
pkFile, null, commands.toString(), 10000, 10000, 60000);
|
||||
return result.first();
|
||||
} catch (Exception e) {
|
||||
String msg = String.format("Failed to taint control nodes on : %s : %s", kubernetesCluster.getName(), e.getMessage());
|
||||
logMessage(Level.ERROR, msg, e);
|
||||
return false;
|
||||
Pair<Boolean, String> result = SshHelper.sshExecute(publicIpAddress, sshPort, getControlNodeLoginUser(),
|
||||
pkFile, null, commands.toString(), 10000, 10000, 60000);
|
||||
return result.first();
|
||||
} catch (Exception e) {
|
||||
String msg = String.format("Failed to taint control nodes on : %s : %s", kubernetesCluster.getName(), e.getMessage());
|
||||
logMessage(Level.ERROR, msg, e);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(5 * 1000L);
|
||||
} catch (InterruptedException ie) {
|
||||
LOGGER.error(String.format("Error while attempting to taint nodes on Kubernetes cluster: %s", kubernetesCluster.getName()), ie);
|
||||
}
|
||||
retryCounter++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean deployProvider() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.dc.ASNumberVO;
|
||||
import com.cloud.dc.BGPService;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.dao.ASNumberDao;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
|
|
@ -63,6 +67,10 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod
|
|||
protected AccountManager accountManager;
|
||||
@Inject
|
||||
private AnnotationDao annotationDao;
|
||||
@Inject
|
||||
private ASNumberDao asNumberDao;
|
||||
@Inject
|
||||
private BGPService bgpService;
|
||||
|
||||
private List<KubernetesClusterVmMapVO> clusterVMs;
|
||||
|
||||
|
|
@ -130,6 +138,7 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod
|
|||
Account owner = accountManager.getAccount(network.getAccountId());
|
||||
User callerUser = accountManager.getActiveUser(CallContext.current().getCallingUserId());
|
||||
ReservationContext context = new ReservationContextImpl(null, null, callerUser, owner);
|
||||
releaseASNumber(kubernetesCluster.getZoneId(), kubernetesCluster.getNetworkId());
|
||||
boolean networkDestroyed = networkMgr.destroyNetwork(kubernetesCluster.getNetworkId(), context, true);
|
||||
if (!networkDestroyed) {
|
||||
String msg = String.format("Failed to destroy network : %s as part of Kubernetes cluster : %s cleanup", network.getName(), kubernetesCluster.getName());
|
||||
|
|
@ -143,6 +152,15 @@ public class KubernetesClusterDestroyWorker extends KubernetesClusterResourceMod
|
|||
}
|
||||
}
|
||||
|
||||
private void releaseASNumber(Long zoneId, long networkId) {
|
||||
DataCenter zone = dataCenterDao.findById(zoneId);
|
||||
ASNumberVO asNumber = asNumberDao.findByZoneAndNetworkId(zone.getId(), networkId);
|
||||
if (asNumber != null) {
|
||||
LOGGER.debug(String.format("Releasing AS number %s from network %s", asNumber.getAsNumber(), networkId));
|
||||
bgpService.releaseASNumber(zone.getId(), asNumber.getAsNumber());
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteKubernetesClusterIsolatedNetworkRules(Network network, List<Long> removedVmIds) throws ManagementServerException {
|
||||
IpAddress publicIp = getNetworkSourceNatIp(network);
|
||||
if (publicIp == null) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.cloud.exception.NetworkRuleConflictException;
|
|||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.network.vpc.NetworkACL;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.user.UserDataVO;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.framework.ca.Certificate;
|
||||
|
|
@ -140,7 +141,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
|
||||
private Pair<String, String> getKubernetesControlNodeConfig(final String controlNodeIp, final String serverIp,
|
||||
final List<Network.IpAddresses> etcdIps, final String hostName, final boolean haSupported,
|
||||
final boolean ejectIso) throws IOException {
|
||||
final boolean ejectIso, final boolean externalCni) throws IOException {
|
||||
String k8sControlNodeConfig = readResourceFile("/conf/k8s-control-node.yml");
|
||||
final String apiServerCert = "{{ k8s_control_node.apiserver.crt }}";
|
||||
final String apiServerKey = "{{ k8s_control_node.apiserver.key }}";
|
||||
|
|
@ -157,6 +158,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
final String k8sApiPort = "{{ k8s.api_server_port }}";
|
||||
final String certSans = "{{ k8s_control.server_ips }}";
|
||||
final String k8sCertificate = "{{ k8s_control.certificate_key }}";
|
||||
final String externalCniPlugin = "{{ k8s.external.cni.plugin }}";
|
||||
|
||||
final List<String> addresses = new ArrayList<>();
|
||||
addresses.add(controlNodeIp);
|
||||
|
|
@ -207,6 +209,7 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
k8sControlNodeConfig = k8sControlNodeConfig.replace(k8sApiPort, String.valueOf(CLUSTER_API_PORT));
|
||||
k8sControlNodeConfig = k8sControlNodeConfig.replace(certSans, String.format("- %s", serverIp));
|
||||
k8sControlNodeConfig = k8sControlNodeConfig.replace(k8sCertificate, KubernetesClusterUtil.generateClusterHACertificateKey(kubernetesCluster));
|
||||
k8sControlNodeConfig = k8sControlNodeConfig.replace(externalCniPlugin, String.valueOf(externalCni));
|
||||
|
||||
k8sControlNodeConfig = updateKubeConfigWithRegistryDetails(k8sControlNodeConfig);
|
||||
|
||||
|
|
@ -238,34 +241,47 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
String suffix = Long.toHexString(System.currentTimeMillis());
|
||||
String hostName = String.format("%s-control-%s", kubernetesClusterNodeNamePrefix, suffix);
|
||||
boolean haSupported = isKubernetesVersionSupportsHA();
|
||||
Long userDataId = kubernetesCluster.getCniConfigId();
|
||||
Pair<String, String> k8sControlNodeConfigAndControlIp = new Pair<>(null, null);
|
||||
try {
|
||||
k8sControlNodeConfigAndControlIp = getKubernetesControlNodeConfig(controlNodeIp, serverIp, etcdIps, hostName, haSupported, Hypervisor.HypervisorType.VMware.equals(clusterTemplate.getHypervisorType()));
|
||||
k8sControlNodeConfigAndControlIp = getKubernetesControlNodeConfig(controlNodeIp, serverIp, etcdIps, hostName, haSupported, Hypervisor.HypervisorType.VMware.equals(clusterTemplate.getHypervisorType()), Objects.nonNull(userDataId));
|
||||
} catch (IOException e) {
|
||||
logAndThrow(Level.ERROR, "Failed to read Kubernetes control node configuration file", e);
|
||||
}
|
||||
String k8sControlNodeConfig = k8sControlNodeConfigAndControlIp.first();
|
||||
String base64UserData = Base64.encodeBase64String(k8sControlNodeConfig.getBytes(com.cloud.utils.StringUtils.getPreferredCharset()));
|
||||
if (Objects.nonNull(userDataId)) {
|
||||
logger.info("concatenating userdata");
|
||||
UserDataVO cniConfigVo = userDataDao.findById(userDataId);
|
||||
String cniConfig = new String(Base64.decodeBase64(cniConfigVo.getUserData()));
|
||||
if (Objects.nonNull(asNumber)) {
|
||||
cniConfig = substituteASNumber(cniConfig, asNumber);
|
||||
cniConfig = Base64.encodeBase64String(cniConfig.getBytes(com.cloud.utils.StringUtils.getPreferredCharset()));
|
||||
}
|
||||
base64UserData = userDataManager.concatenateUserData(base64UserData, cniConfig, null);
|
||||
}
|
||||
|
||||
List<String> keypairs = new ArrayList<String>();
|
||||
if (StringUtils.isNotBlank(kubernetesCluster.getKeyPair())) {
|
||||
keypairs.add(kubernetesCluster.getKeyPair());
|
||||
}
|
||||
|
||||
Long affinityGroupId = getExplicitAffinityGroup(domainId, accountId);
|
||||
String userDataDetails = kubernetesCluster.getCniConfigDetails();
|
||||
if (kubernetesCluster.getSecurityGroupId() != null &&
|
||||
networkModel.checkSecurityGroupSupportForNetwork(owner, zone, networkIds,
|
||||
List.of(kubernetesCluster.getSecurityGroupId()))) {
|
||||
List<Long> securityGroupIds = new ArrayList<>();
|
||||
securityGroupIds.add(kubernetesCluster.getSecurityGroupId());
|
||||
controlVm = userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, securityGroupIds, owner,
|
||||
hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, null, null, keypairs,
|
||||
hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST,base64UserData, userDataId, userDataDetails, keypairs,
|
||||
requestedIps, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null,
|
||||
null, true, null, UserVmManager.CKS_NODE);
|
||||
} else {
|
||||
controlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, controlNodeTemplate, networkIds, owner,
|
||||
hostName, hostName, null, null, null,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, null, null, keypairs,
|
||||
Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, userDataId, userDataDetails, keypairs,
|
||||
requestedIps, addrs, null, null, Objects.nonNull(affinityGroupId) ?
|
||||
Collections.singletonList(affinityGroupId) : null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null);
|
||||
}
|
||||
|
|
@ -275,6 +291,13 @@ public class KubernetesClusterStartWorker extends KubernetesClusterResourceModif
|
|||
return controlVm;
|
||||
}
|
||||
|
||||
private String substituteASNumber(String cniConfig, Long asNumber) {
|
||||
final String asNumberKey = "{{ AS_NUMBER }}";
|
||||
cniConfig = cniConfig.replace(asNumberKey, String.valueOf(asNumber));
|
||||
return cniConfig;
|
||||
|
||||
}
|
||||
|
||||
private String getKubernetesAdditionalControlNodeConfig(final String joinIp, final boolean ejectIso) throws IOException {
|
||||
String k8sControlNodeConfig = readResourceFile("/conf/k8s-control-node-add.yml");
|
||||
final String joinIpKey = "{{ k8s_control_node.join_ip }}";
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package com.cloud.kubernetes.cluster.actionworkers;
|
|||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.cloud.kubernetes.cluster.KubernetesClusterVmMapVO;
|
||||
|
|
@ -67,12 +68,12 @@ public class KubernetesClusterUpgradeWorker extends KubernetesClusterActionWorke
|
|||
String nodeAddress = (index > 0 && sshPort == 22) ? vm.getPrivateIpAddress() : publicIpAddress;
|
||||
SshHelper.scpTo(nodeAddress, nodeSshPort, getControlNodeLoginUser(), sshKeyFile, null,
|
||||
"~/", upgradeScriptFile.getAbsolutePath(), "0755");
|
||||
String cmdStr = String.format("sudo ./%s %s %s %s %s",
|
||||
String cmdStr = String.format("sudo ./%s %s %s %s %s %s",
|
||||
upgradeScriptFile.getName(),
|
||||
upgradeVersion.getSemanticVersion(),
|
||||
index == 0 ? "true" : "false",
|
||||
KubernetesVersionManagerImpl.compareSemanticVersions(upgradeVersion.getSemanticVersion(), "1.15.0") < 0 ? "true" : "false",
|
||||
Hypervisor.HypervisorType.VMware.equals(vm.getHypervisorType()));
|
||||
Hypervisor.HypervisorType.VMware.equals(vm.getHypervisorType()), Objects.isNull(kubernetesCluster.getCniConfigId()));
|
||||
return SshHelper.sshExecute(nodeAddress, nodeSshPort, getControlNodeLoginUser(), sshKeyFile, null,
|
||||
cmdStr,
|
||||
10000, 10000, 10 * 60 * 1000);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import org.apache.cloudstack.api.response.KubernetesSupportedVersionResponse;
|
|||
import org.apache.cloudstack.api.response.NetworkResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.UserDataResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
|
|
@ -193,6 +194,15 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
|
|||
@Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which the CKS cluster is to be deployed. This is required if the zone in which the CKS cluster is being deployed has clusters with different hypervisor types.")
|
||||
private String hypervisor;
|
||||
|
||||
@Parameter(name = ApiConstants.CNI_CONFIG_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.19.0")
|
||||
private Long cniConfigId;
|
||||
|
||||
@Parameter(name = ApiConstants.CNI_CONFIG_DETAILS, type = CommandType.MAP,
|
||||
description = "used to specify the parameters values for the variables in userdata. " +
|
||||
"Example: cniconfigdetails[0].key=accesskey&cniconfigdetails[0].value=s389ddssaa&" +
|
||||
"cniconfigdetails[1].key=secretkey&cniconfigdetails[1].value=8dshfsss", since = "4.19.0")
|
||||
private Map cniConfigDetails;
|
||||
|
||||
@Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, description="the AS Number of the network")
|
||||
private Long asNumber;
|
||||
|
||||
|
|
@ -349,6 +359,14 @@ public class CreateKubernetesClusterCmd extends BaseAsyncCreateCmd {
|
|||
return asNumber;
|
||||
}
|
||||
|
||||
public Map getCniConfigDetails() {
|
||||
return convertDetailsToMap(cniConfigDetails);
|
||||
}
|
||||
|
||||
public Long getCniConfigId() {
|
||||
return cniConfigId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
## template: jinja
|
||||
#cloud-config
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
|
|
@ -279,6 +280,7 @@ write_files:
|
|||
fi
|
||||
|
||||
EXTERNAL_ETCD_NODES={{ etcd.unstacked_etcd }}
|
||||
EXTERNAL_CNI_PLUGIN={{ k8s.external.cni.plugin }}
|
||||
MAX_SETUP_CRUCIAL_CMD_ATTEMPTS=3
|
||||
crucial_cmd_attempts=1
|
||||
while true; do
|
||||
|
|
@ -320,7 +322,9 @@ write_files:
|
|||
if [ -d "$K8S_CONFIG_SCRIPTS_COPY_DIR" ]; then
|
||||
### Network, dashboard configs available offline ###
|
||||
echo "Offline configs are available!"
|
||||
/opt/bin/kubectl apply -f ${K8S_CONFIG_SCRIPTS_COPY_DIR}/network.yaml
|
||||
if [[ ${EXTERNAL_CNI_PLUGIN} == false ]]; then
|
||||
/opt/bin/kubectl apply -f ${K8S_CONFIG_SCRIPTS_COPY_DIR}/network.yaml
|
||||
fi
|
||||
/opt/bin/kubectl apply -f ${K8S_CONFIG_SCRIPTS_COPY_DIR}/dashboard.yaml
|
||||
rm -rf "${K8S_CONFIG_SCRIPTS_COPY_DIR}"
|
||||
else
|
||||
|
|
@ -335,6 +339,7 @@ write_files:
|
|||
sudo touch /home/cloud/success
|
||||
echo "true" > /home/cloud/success
|
||||
|
||||
{% if registry is defined %}
|
||||
- path: /opt/bin/setup-containerd
|
||||
permissions: '0755'
|
||||
owner: root:root
|
||||
|
|
@ -352,6 +357,7 @@ write_files:
|
|||
echo "Restarting containerd service"
|
||||
systemctl daemon-reload
|
||||
systemctl restart containerd
|
||||
{% endif %}
|
||||
|
||||
- path: /etc/systemd/system/deploy-kube-system.service
|
||||
permissions: '0755'
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
# Version 1.14 and below needs extra flags with kubeadm upgrade node
|
||||
if [ $# -lt 4 ]; then
|
||||
echo "Invalid input. Valid usage: ./upgrade-kubernetes.sh UPGRADE_VERSION IS_CONTROL_NODE IS_OLD_VERSION IS_EJECT_ISO"
|
||||
echo "Invalid input. Valid usage: ./upgrade-kubernetes.sh UPGRADE_VERSION IS_CONTROL_NODE IS_OLD_VERSION IS_EJECT_ISO IS_EXTERNAL_CNI"
|
||||
echo "eg: ./upgrade-kubernetes.sh 1.16.3 true false false"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -35,6 +35,10 @@ EJECT_ISO_FROM_OS=false
|
|||
if [ $# -gt 3 ]; then
|
||||
EJECT_ISO_FROM_OS="${4}"
|
||||
fi
|
||||
EXTERNAL_CNI=false
|
||||
if [ $# -gt 4 ]; then
|
||||
EXTERNAL_CNI="${5}"
|
||||
fi
|
||||
|
||||
export PATH=$PATH:/opt/bin
|
||||
if [[ "$PATH" != *:/usr/sbin && "$PATH" != *:/usr/sbin:* ]]; then
|
||||
|
|
@ -144,7 +148,9 @@ if [ -d "$BINARIES_DIR" ]; then
|
|||
systemctl restart kubelet
|
||||
|
||||
if [ "${IS_MAIN_CONTROL}" == 'true' ]; then
|
||||
/opt/bin/kubectl apply -f ${BINARIES_DIR}/network.yaml
|
||||
if [[ ${EXTERNAL_CNI} == true ]]; then
|
||||
/opt/bin/kubectl apply -f ${BINARIES_DIR}/network.yaml
|
||||
fi
|
||||
/opt/bin/kubectl apply -f ${BINARIES_DIR}/dashboard.yaml
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -5411,9 +5411,13 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||
response.setZoneName(zone.getName());
|
||||
response.setAsNumber(asn.getAsNumber());
|
||||
ASNumberRangeVO range = asNumberRangeDao.findById(asn.getAsNumberRangeId());
|
||||
response.setAsNumberRangeId(range.getUuid());
|
||||
String rangeText = String.format("%s-%s", range.getStartASNumber(), range.getEndASNumber());
|
||||
response.setAsNumberRange(rangeText);
|
||||
if (Objects.nonNull(range)) {
|
||||
response.setAsNumberRangeId(range.getUuid());
|
||||
String rangeText = String.format("%s-%s", range.getStartASNumber(), range.getEndASNumber());
|
||||
response.setAsNumberRange(rangeText);
|
||||
} else {
|
||||
s_logger.info("is null for as number: "+ asn.getAsNumber());
|
||||
}
|
||||
response.setAllocated(asn.getAllocatedTime());
|
||||
response.setAllocationState(asn.isAllocated() ? "Allocated" : "Free");
|
||||
if (asn.getVpcId() != null) {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ import javax.inject.Inject;
|
|||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.bgp.BGPService;
|
||||
import com.cloud.dc.ASNumberVO;
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.dao.ASNumberDao;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import com.cloud.network.dao.NsxProviderDao;
|
||||
import com.cloud.network.dao.PublicIpQuarantineDao;
|
||||
|
|
@ -421,6 +423,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
RoutedIpv4Manager routedIpv4Manager;
|
||||
@Inject
|
||||
private BGPService bgpService;
|
||||
@Inject
|
||||
private ASNumberDao asNumberDao;
|
||||
|
||||
List<InternalLoadBalancerElementService> internalLoadBalancerElementServices = new ArrayList<>();
|
||||
Map<String, InternalLoadBalancerElementService> internalLoadBalancerElementServiceMap = new HashMap<>();
|
||||
|
|
@ -1726,6 +1730,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
routedIpv4Manager.validateBgpPeers(owner, zone.getId(), bgpPeerIds);
|
||||
}
|
||||
|
||||
// Check AS number if provided
|
||||
ASNumberVO asNumberVO = checkAndSelectASNumber(asNumber, zone, ntwkOff, asNumberDao);
|
||||
|
||||
if (ipv4) {
|
||||
// For non-root admins check cidr limit - if it's allowed by global config value
|
||||
if (!_accountMgr.isRootAdmin(caller.getId()) && cidr != null) {
|
||||
|
|
@ -1836,6 +1843,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
routedIpv4Manager.persistBgpPeersForGuestNetwork(network.getId(), bgpPeerIds);
|
||||
}
|
||||
|
||||
if (asNumberVO != null) {
|
||||
bgpService.allocateASNumber(zone.getId(), asNumberVO.getAsNumber(), network.getId(), network.getVpcId());
|
||||
}
|
||||
|
||||
// if the network offering has persistent set to true, implement the network
|
||||
if (ntwkOff.isPersistent()) {
|
||||
return implementedNetworkInCreation(caller, zone, network);
|
||||
|
|
@ -1847,6 +1858,26 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
|||
return !networkOffering.isForVpc() && NetworkOffering.RoutingMode.Dynamic == networkOffering.getRoutingMode();
|
||||
}
|
||||
|
||||
public static ASNumberVO checkAndSelectASNumber(Long asNumber, DataCenter zone, NetworkOffering networkOffering, ASNumberDao asNumberDao) {
|
||||
if (NetworkOffering.RoutingMode.Dynamic != networkOffering.getRoutingMode()) {
|
||||
return null;
|
||||
}
|
||||
return BooleanUtils.toBoolean(networkOffering.isSpecifyAsNumber()) ?
|
||||
validateASNumber(asNumber, asNumberDao) :
|
||||
asNumberDao.findOneByAllocationStateAndZone(zone.getId(), false);
|
||||
}
|
||||
|
||||
protected static ASNumberVO validateASNumber(Long asNumber, ASNumberDao asNumberDao) {
|
||||
if (asNumber == null) {
|
||||
return null;
|
||||
}
|
||||
ASNumberVO asNumberVO = asNumberDao.findByAsNumber(asNumber);
|
||||
if (asNumberVO == null || asNumberVO.isAllocated()) {
|
||||
throw new InvalidParameterValueException(String.format("The AS Number %s is already allocated, please select a free AS Number", asNumber));
|
||||
}
|
||||
return asNumberVO;
|
||||
}
|
||||
|
||||
private void validateNetworkCreationSupported(long zoneId, String zoneName, GuestType guestType) {
|
||||
NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId);
|
||||
if (Objects.nonNull(nsxProviderVO) && GuestType.L2.equals(guestType)) {
|
||||
|
|
|
|||
|
|
@ -519,9 +519,12 @@ import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
|
|||
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissionsCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.BaseRegisterUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.LinkUserDataToTemplateCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.ListCniConfigurationCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.RegisterCniConfigurationCmd;
|
||||
import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.AddIpToVmNicCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
|
||||
|
|
@ -4030,6 +4033,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
cmdList.add(DeleteUserDataCmd.class);
|
||||
cmdList.add(ListUserDataCmd.class);
|
||||
cmdList.add(LinkUserDataToTemplateCmd.class);
|
||||
cmdList.add(RegisterCniConfigurationCmd.class);
|
||||
cmdList.add(ListCniConfigurationCmd.class);
|
||||
|
||||
//object store APIs
|
||||
cmdList.add(AddObjectStoragePoolCmd.class);
|
||||
|
|
@ -4829,7 +4834,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<? extends UserData>, Integer> listUserDatas(final ListUserDataCmd cmd) {
|
||||
public Pair<List<? extends UserData>, Integer> listUserDatas(final ListUserDataCmd cmd, final boolean forCks) {
|
||||
final Long id = cmd.getId();
|
||||
final String name = cmd.getName();
|
||||
final String keyword = cmd.getKeyword();
|
||||
|
|
@ -4849,6 +4854,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE);
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
sb.and("forCks", sb.entity().isForCks(), SearchCriteria.Op.EQ);
|
||||
final SearchCriteria<UserDataVO> sc = sb.create();
|
||||
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
||||
|
||||
|
|
@ -4864,24 +4871,33 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
sc.setParameters("keyword", "%" + keyword + "%");
|
||||
}
|
||||
|
||||
sc.setParameters("forCks", forCks);
|
||||
|
||||
final Pair<List<UserDataVO>, Integer> result = userDataDao.searchAndCount(sc, searchFilter);
|
||||
return new Pair<>(result.first(), result.second());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_REGISTER_USER_DATA, eventDescription = "registering userdata", async = true)
|
||||
public UserData registerUserData(final RegisterUserDataCmd cmd) {
|
||||
public UserData registerUserData(final BaseRegisterUserDataCmd cmd) {
|
||||
final Account owner = getOwner(cmd);
|
||||
checkForUserDataByName(cmd, owner);
|
||||
checkForUserData(cmd, owner);
|
||||
|
||||
final String name = cmd.getName();
|
||||
String userdata = cmd.getUserData();
|
||||
|
||||
boolean forCks = false;
|
||||
String userdata = null;
|
||||
if (cmd instanceof RegisterUserDataCmd) {
|
||||
userdata = ((RegisterUserDataCmd) cmd).getUserData();
|
||||
checkForUserData(((RegisterUserDataCmd) cmd), owner);
|
||||
} else {
|
||||
userdata = ((RegisterCniConfigurationCmd) cmd).getCniConfig();
|
||||
forCks = true;
|
||||
}
|
||||
final String params = cmd.getParams();
|
||||
|
||||
userdata = userDataManager.validateUserData(userdata, cmd.getHttpMethod());
|
||||
|
||||
return createAndSaveUserData(name, userdata, params, owner);
|
||||
return createAndSaveUserData(name, userdata, params, owner, forCks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -4901,7 +4917,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
* @param owner
|
||||
* @throws InvalidParameterValueException
|
||||
*/
|
||||
private void checkForUserDataByName(final RegisterUserDataCmd cmd, final Account owner) throws InvalidParameterValueException {
|
||||
private void checkForUserDataByName(final BaseRegisterUserDataCmd cmd, final Account owner) throws InvalidParameterValueException {
|
||||
final UserDataVO userData = userDataDao.findByName(owner.getAccountId(), owner.getDomainId(), cmd.getName());
|
||||
if (userData != null) {
|
||||
throw new InvalidParameterValueException(String.format("A userdata with name %s already exists for this account.", cmd.getName()));
|
||||
|
|
@ -4970,7 +4986,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
* @param cmd
|
||||
* @return Account
|
||||
*/
|
||||
protected Account getOwner(final RegisterUserDataCmd cmd) {
|
||||
protected Account getOwner(final BaseRegisterUserDataCmd cmd) {
|
||||
final Account caller = getCaller();
|
||||
return _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
|
||||
}
|
||||
|
|
@ -4998,7 +5014,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
return newPair;
|
||||
}
|
||||
|
||||
private UserData createAndSaveUserData(final String name, final String userdata, final String params, final Account owner) {
|
||||
private UserData createAndSaveUserData(final String name, final String userdata, final String params, final Account owner, final boolean isForCks) {
|
||||
final UserDataVO userDataVO = new UserDataVO();
|
||||
|
||||
userDataVO.setAccountId(owner.getAccountId());
|
||||
|
|
@ -5006,6 +5022,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
|
|||
userDataVO.setName(name);
|
||||
userDataVO.setUserData(userdata);
|
||||
userDataVO.setParams(params);
|
||||
userDataVO.setForCks(isForCks);
|
||||
|
||||
userDataDao.persist(userDataVO);
|
||||
|
||||
|
|
|
|||
|
|
@ -479,7 +479,7 @@ public class ManagementServerImplTest {
|
|||
Pair<List<UserDataVO>, Integer> result = new Pair(userDataList, 1);
|
||||
when(_userDataDao.searchAndCount(nullable(SearchCriteria.class), nullable(Filter.class))).thenReturn(result);
|
||||
|
||||
Pair<List<? extends UserData>, Integer> userdataResultList = spy.listUserDatas(cmd);
|
||||
Pair<List<? extends UserData>, Integer> userdataResultList = spy.listUserDatas(cmd, false);
|
||||
|
||||
Assert.assertEquals(userdataResultList.first().get(0), userDataList.get(0));
|
||||
}
|
||||
|
|
@ -512,7 +512,7 @@ public class ManagementServerImplTest {
|
|||
Pair<List<UserDataVO>, Integer> result = new Pair(userDataList, 1);
|
||||
when(_userDataDao.searchAndCount(nullable(SearchCriteria.class), nullable(Filter.class))).thenReturn(result);
|
||||
|
||||
Pair<List<? extends UserData>, Integer> userdataResultList = spy.listUserDatas(cmd);
|
||||
Pair<List<? extends UserData>, Integer> userdataResultList = spy.listUserDatas(cmd, false);
|
||||
|
||||
Assert.assertEquals(userdataResultList.first().get(0), userDataList.get(0));
|
||||
}
|
||||
|
|
@ -545,7 +545,7 @@ public class ManagementServerImplTest {
|
|||
Pair<List<UserDataVO>, Integer> result = new Pair(userDataList, 1);
|
||||
when(_userDataDao.searchAndCount(nullable(SearchCriteria.class), nullable(Filter.class))).thenReturn(result);
|
||||
|
||||
Pair<List<? extends UserData>, Integer> userdataResultList = spy.listUserDatas(cmd);
|
||||
Pair<List<? extends UserData>, Integer> userdataResultList = spy.listUserDatas(cmd, false);
|
||||
|
||||
Assert.assertEquals(userdataResultList.first().get(0), userDataList.get(0));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1874,7 +1874,8 @@
|
|||
"label.region": "Region",
|
||||
"label.register.oauth": "Register OAuth",
|
||||
"label.register.template": "Register Template",
|
||||
"label.register.user.data": "Register a userdata",
|
||||
"label.register.user.data": "Register User Data",
|
||||
"label.register.cni.config": "Register CNI Configuration",
|
||||
"label.reinstall.vm": "Reinstall Instance",
|
||||
"label.reject": "Reject",
|
||||
"label.related": "Related",
|
||||
|
|
@ -2648,6 +2649,8 @@
|
|||
"label.bucket.policy": "Bucket Policy",
|
||||
"label.usersecretkey": "Secret Key",
|
||||
"label.create.bucket": "Create Bucket",
|
||||
"label.cniconfiguration": "CNI Configuration",
|
||||
"label.cniconfigparams": "CNI Configuration parameters",
|
||||
"message.acquire.ip.failed": "Failed to acquire IP.",
|
||||
"message.action.acquire.ip": "Please confirm that you want to acquire new IP.",
|
||||
"message.action.cancel.maintenance": "Your host has been successfully canceled for maintenance. This process can take up to several minutes.",
|
||||
|
|
@ -3020,6 +3023,7 @@
|
|||
"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you would like to add to this Instance.",
|
||||
"message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server. We will add the first one now. Secondary storage stores Instance Templates, ISO images, and Instance disk volume Snapshots. This server must be available to all hosts in the zone.<br/><br/>Provide the IP address and exported path.",
|
||||
"message.desc.register.user.data": "Please fill in the following data to register a User data.",
|
||||
"message.desc.register.cni.config": "Please fill in the following data to register CNI Configuration as user data.",
|
||||
"message.desc.registered.user.data": "Registered a User Data.",
|
||||
"message.desc.zone": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.",
|
||||
"message.desc.zone.edge": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. An edge zone consists of one or more hosts (each of which provides local storage as primary storage servers). Only shared and L2 Networks can be deployed in such zones and functionalities that require secondary storages are not supported.",
|
||||
|
|
|
|||
|
|
@ -44,7 +44,9 @@
|
|||
<template #renderItem="{item}">
|
||||
<a-list-item v-if="(item in dataResource && !customDisplayItems.includes(item)) || (offeringDetails.includes(item) && dataResource.serviceofferingdetails)">
|
||||
<div style="width: 100%">
|
||||
<strong>{{ item === 'service' ? $t('label.supportedservices') : $t(getDetailTitle(item)) }}</strong>
|
||||
<strong>{{ item === 'service' ? $t('label.supportedservices') :
|
||||
$route.meta.name === 'cniconfiguration' && item === 'userdata' ? $t('label.' + String($route.meta.name).toLowerCase()) :
|
||||
$t(getDetailTitle(item)) }}</strong>
|
||||
<br/>
|
||||
<div v-if="Array.isArray(dataResource[item]) && item === 'service'">
|
||||
<div v-for="(service, idx) in dataResource[item]" :key="idx">
|
||||
|
|
@ -96,6 +98,9 @@
|
|||
<div v-else-if="$route.meta.name === 'userdata' && item === 'userdata'">
|
||||
<div style="white-space: pre-wrap;"> {{ decodeUserData(dataResource.userdata)}} </div>
|
||||
</div>
|
||||
<div v-else-if="$route.meta.name === 'cniconfiguration' && item === 'userdata'">
|
||||
<div style="white-space: pre-wrap;"> {{ dataResource.userdata}} </div>
|
||||
</div>
|
||||
<div v-else-if="$route.meta.name === 'guestnetwork' && item === 'egressdefaultpolicy'">
|
||||
{{ dataResource[item]? $t('message.egress.rules.allow') : $t('message.egress.rules.deny') }}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -988,6 +988,83 @@ export default {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'cniconfiguration',
|
||||
title: 'label.cniconfiguration',
|
||||
icon: 'solution-outlined',
|
||||
docHelp: 'adminguide/virtual_machines.html#user-data-and-meta-data',
|
||||
permission: ['listCniConfiguration'],
|
||||
columns: () => {
|
||||
var fields = ['name', 'id']
|
||||
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||
fields.push('account')
|
||||
if (store.getters.listAllProjects) {
|
||||
fields.push('project')
|
||||
}
|
||||
fields.push('domain')
|
||||
} else if (store.getters.listAllProjects) {
|
||||
fields.push('project')
|
||||
}
|
||||
return fields
|
||||
},
|
||||
resourceType: 'UserData',
|
||||
details: ['id', 'name', 'userdata', 'account', 'domain', 'params'],
|
||||
related: [{
|
||||
name: 'vm',
|
||||
title: 'label.instances',
|
||||
param: 'userdata'
|
||||
}],
|
||||
tabs: [
|
||||
{
|
||||
name: 'details',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||
},
|
||||
{
|
||||
name: 'comments',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/AnnotationsTab.vue')))
|
||||
}
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
api: 'registerCniConfiguration',
|
||||
icon: 'plus-outlined',
|
||||
label: 'label.register.cni.config',
|
||||
docHelp: 'adminguide/virtual_machines.html#creating-the-ssh-keypair',
|
||||
listView: true,
|
||||
popup: true,
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/compute/RegisterUserData.vue')))
|
||||
},
|
||||
{
|
||||
api: 'deleteUserData',
|
||||
icon: 'delete-outlined',
|
||||
label: 'label.remove.user.data',
|
||||
message: 'message.please.confirm.remove.user.data',
|
||||
dataView: true,
|
||||
args: ['id', 'account', 'domainid'],
|
||||
mapping: {
|
||||
id: {
|
||||
value: (record, params) => { return record.id }
|
||||
},
|
||||
account: {
|
||||
value: (record, params) => { return record.account }
|
||||
},
|
||||
domainid: {
|
||||
value: (record, params) => { return record.domainid }
|
||||
}
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection, values, record) => {
|
||||
return selection.map(x => {
|
||||
const data = record.filter(y => { return y.id === x })
|
||||
return {
|
||||
id: x, account: data[0].account, domainid: data[0].domainid
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'affinitygroup',
|
||||
title: 'label.affinity.groups',
|
||||
|
|
|
|||
|
|
@ -918,6 +918,10 @@ export default {
|
|||
}
|
||||
|
||||
this.loading = true
|
||||
if (this.$route.path.startsWith('/cniconfiguration')) {
|
||||
params.forcks = true
|
||||
console.log('here')
|
||||
}
|
||||
if (this.$route.params && this.$route.params.id) {
|
||||
params.id = this.$route.params.id
|
||||
if (['listSSHKeyPairs'].includes(this.apiName)) {
|
||||
|
|
@ -951,6 +955,8 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
console.log(params)
|
||||
|
||||
if (this.$store.getters.listAllProjects && !this.projectView) {
|
||||
params.projectid = '-1'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,6 +329,60 @@
|
|||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.advancedmode && isASNumberRequired()" name="asnumber" ref="asnumber">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.asnumber')" :tooltip="apiParams.asnumber.description"/>
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="form.asnumber"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="asNumberLoading"
|
||||
:placeholder="apiParams.asnumber.description"
|
||||
@change="val => { handleASNumberChange(val) }">
|
||||
<a-select-option v-for="(opt, optIndex) in asNumbersZone" :key="optIndex" :label="opt.asnumber">
|
||||
{{ opt.asnumber }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.advancedmode" name="cniconfigurationid" ref="cniconfigurationid">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.cniconfiguration')" :tooltip="$t('label.cniconfiguration')"/>
|
||||
</template>
|
||||
<user-data-selection
|
||||
:items="cniConfigurationData"
|
||||
:row-count="cniConfigurationData.length"
|
||||
:zoneId="zoneId"
|
||||
:loading="cniConfigLoading"
|
||||
:preFillContent="dataPreFill"
|
||||
:showSearch="false"
|
||||
@select-user-data-item="($event) => updateCniConfig($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('userData', $event)"
|
||||
/>
|
||||
<div v-if="cniConfigParams.length > 0">
|
||||
<a-input-group>
|
||||
<a-table
|
||||
size="small"
|
||||
style="overflow-y: auto"
|
||||
:columns="userDataParamCols"
|
||||
:dataSource="cniConfigParams"
|
||||
:pagination="false"
|
||||
:rowKey="record => record.key">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'value'">
|
||||
<div v-if="record.key === 'AS_NUMBER'">
|
||||
<a-input style="width: 100%;text-wrap: wrap;" :disabled="true" value="Value is obtained from the network or is automatically obtained at the time of cluster creation" />
|
||||
</div>
|
||||
<a-input v-else v-model:value="cniConfigValues[record.key]" />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-input-group>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<!-- Experimentation Features -->
|
||||
<div v-if="$store.getters.features.kubernetesclusterexperimentalfeaturesenabled">
|
||||
|
|
@ -380,13 +434,15 @@ import { api } from '@/api'
|
|||
import { mixinForm } from '@/utils/mixin'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
import UserDataSelection from '@views/compute/wizard/UserDataSelection'
|
||||
|
||||
export default {
|
||||
name: 'CreateKubernetesCluster',
|
||||
mixins: [mixinForm],
|
||||
components: {
|
||||
TooltipLabel,
|
||||
ResourceIcon
|
||||
ResourceIcon,
|
||||
UserDataSelection
|
||||
},
|
||||
props: {},
|
||||
data () {
|
||||
|
|
@ -407,7 +463,28 @@ export default {
|
|||
templates: [],
|
||||
templateLoading: false,
|
||||
selectedZoneHypervisors: [],
|
||||
hypervisorLoading: false
|
||||
hypervisorLoading: false,
|
||||
configLoading: false,
|
||||
cniConfigurationData: [],
|
||||
cniConfigLoading: false,
|
||||
cniConfigParams: [],
|
||||
cniConfigValues: {},
|
||||
userDataParamCols: [
|
||||
{
|
||||
title: this.$t('label.key'),
|
||||
dataIndex: 'key'
|
||||
},
|
||||
{
|
||||
title: this.$t('label.value'),
|
||||
dataIndex: 'value',
|
||||
key: 'value'
|
||||
}
|
||||
],
|
||||
cksNetworkOfferingName: null,
|
||||
cksNetworkOffering: null,
|
||||
asNumbersZone: [],
|
||||
asNumberLoading: false,
|
||||
selectedAsNumber: 0
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -474,16 +551,25 @@ export default {
|
|||
this.fetchZoneData()
|
||||
this.fetchKeyPairData()
|
||||
this.fetchCksTemplates()
|
||||
this.fetchCKSNetworkOfferingName()
|
||||
this.fetchCniConfigurations()
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
isASNumberRequired () {
|
||||
return !this.isObjectEmpty(this.cksNetworkOffering) && this.cksNetworkOffering.specifyasnumber && this.cksNetworkOffering.routingmode && this.cksNetworkOffering.routingmode.toLowerCase() === 'dynamic'
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
isObjectEmpty (obj) {
|
||||
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||
},
|
||||
isUserAllowedToListCniConfig () {
|
||||
console.log(Boolean('listCniConfiguration' in this.$store.getters.apis))
|
||||
return Boolean('listCniConfiguration' in this.$store.getters.apis)
|
||||
},
|
||||
fetchZoneData () {
|
||||
const params = {}
|
||||
this.zoneLoading = true
|
||||
|
|
@ -507,6 +593,11 @@ export default {
|
|||
this.fetchKubernetesVersionData()
|
||||
this.fetchNetworkData()
|
||||
this.fetchZoneHypervisors()
|
||||
this.fetchZoneASNumbers()
|
||||
},
|
||||
handleASNumberChange (selectedIndex) {
|
||||
this.selectedAsNumber = this.asNumbersZone[selectedIndex].asnumber
|
||||
this.form.asnumber = this.selectedAsNumber
|
||||
},
|
||||
fetchKubernetesVersionData () {
|
||||
this.kubernetesVersions = []
|
||||
|
|
@ -640,6 +731,83 @@ export default {
|
|||
handleZoneHypervisorChange (index) {
|
||||
this.form.hypervisor = index
|
||||
},
|
||||
fetchCKSNetworkOfferingName () {
|
||||
const params = {
|
||||
name: 'cloud.kubernetes.cluster.network.offering'
|
||||
}
|
||||
this.configLoading = true
|
||||
api('listConfigurations', params).then(json => {
|
||||
if (json.listconfigurationsresponse.configuration !== null) {
|
||||
const config = json.listconfigurationsresponse.configuration[0]
|
||||
if (config && config.name === params.name) {
|
||||
this.cksNetworkOfferingName = config.value
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
this.fetchCKSNetworkOffering(this.cksNetworkOfferingName)
|
||||
}).finally(() => {
|
||||
this.configLoading = false
|
||||
})
|
||||
},
|
||||
fetchCKSNetworkOffering (offeringName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const args = {
|
||||
name: offeringName
|
||||
}
|
||||
|
||||
api('listNetworkOfferings', args).then(json => {
|
||||
const listNetworkOfferings = json.listnetworkofferingsresponse.networkoffering || []
|
||||
resolve(listNetworkOfferings)
|
||||
this.cksNetworkOffering = listNetworkOfferings[0] || {}
|
||||
}).catch(error => {
|
||||
resolve(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchZoneASNumbers () {
|
||||
const params = {}
|
||||
params.zoneid = this.selectedZone.id
|
||||
params.isallocated = false
|
||||
api('listASNumbers', params).then(json => {
|
||||
this.asNumbersZone = json.listasnumbersresponse.asnumber
|
||||
})
|
||||
},
|
||||
fetchCniConfigurations () {
|
||||
this.cniConfigLoading = true
|
||||
api('listCniConfiguration', {}).then(
|
||||
response => {
|
||||
const listResponse = response.listcniconfigurationresponse.cniconfig || []
|
||||
if (listResponse) {
|
||||
this.cniConfigurationData = listResponse
|
||||
}
|
||||
}).finally(() => {
|
||||
this.cniConfigLoading = false
|
||||
})
|
||||
},
|
||||
updateCniConfig (id) {
|
||||
if (id === '0') {
|
||||
this.form.cniconfigurationid = undefined
|
||||
return
|
||||
}
|
||||
this.form.cniconfigurationid = id
|
||||
this.cniConfigParams = []
|
||||
api('listCniConfiguration', { id: id }).then(json => {
|
||||
const resp = json?.listcniconfigurationresponse?.cniconfig || []
|
||||
if (resp) {
|
||||
var params = resp[0].params
|
||||
if (params) {
|
||||
var dataParams = params.split(',')
|
||||
}
|
||||
var that = this
|
||||
dataParams.forEach(function (val, index) {
|
||||
that.cniConfigParams.push({
|
||||
id: index,
|
||||
key: val
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
|
|
@ -719,6 +887,21 @@ export default {
|
|||
params.dockerregistryurl = values.dockerregistryurl
|
||||
}
|
||||
|
||||
if (values.cniconfigurationid) {
|
||||
params.cniconfigurationid = values.cniconfigurationid
|
||||
}
|
||||
|
||||
var idx = 0
|
||||
if (this.cniConfigValues) {
|
||||
for (const [key, value] of Object.entries(this.cniConfigValues)) {
|
||||
params['cniconfigdetails[' + idx + '].' + `${key}`] = value
|
||||
idx++
|
||||
}
|
||||
}
|
||||
if ('asnumber' in values && this.isASNumberRequired()) {
|
||||
params.asnumber = values.asnumber
|
||||
}
|
||||
|
||||
api('createKubernetesCluster', params).then(json => {
|
||||
const jobId = json.createkubernetesclusterresponse.jobid
|
||||
this.$pollJob({
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<template>
|
||||
<div class="form-layout">
|
||||
<a-spin :spinning="loading" v-if="!isSubmitted">
|
||||
<p v-html="$t('message.desc.register.user.data')"></p>
|
||||
<p v-html="$route.name === 'userdata' ? $t('message.desc.register.user.data') : $t('message.desc.register.cni.config')"></p>
|
||||
<a-form
|
||||
v-ctrl-enter="handleSubmit"
|
||||
:ref="formRef"
|
||||
|
|
@ -35,20 +35,34 @@
|
|||
:placeholder="apiParams.name.description"
|
||||
v-focus="true" />
|
||||
</a-form-item>
|
||||
<a-form-item name="userdata" ref="userdata">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.userdata')" :tooltip="apiParams.userdata.description"/>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="form.userdata"
|
||||
:placeholder="apiParams.userdata.description"/>
|
||||
</a-form-item>
|
||||
<div v-if="$route.name === 'userdata'">
|
||||
<a-form-item name="userdata" ref="userdata">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.userdata')" :tooltip="apiParams.userdata.description"/>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="form.userdata"
|
||||
:placeholder="apiParams.userdata.description"/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-form-item name="cniconfig" ref="cniconfig">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.cniconfiguration')" :tooltip="apiParams.cniconfig.description"/>
|
||||
</template>
|
||||
<a-textarea
|
||||
v-model:value="form.cniconfig"
|
||||
:placeholder="apiParams.cniconfig.description"/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<a-form-item name="isbase64" ref="isbase64" :label="$t('label.is.base64.encoded')">
|
||||
<a-checkbox v-model:checked="form.isbase64"></a-checkbox>
|
||||
</a-form-item>
|
||||
<a-form-item name="params" ref="params">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.userdataparams')" :tooltip="apiParams.params.description"/>
|
||||
<tooltip-label
|
||||
:title="$route.name === 'userdata' ? $t('label.userdataparams') : $t('label.cniconfigparams')"
|
||||
:tooltip="apiParams.params.description"/>
|
||||
</template>
|
||||
<a-select
|
||||
mode="tags"
|
||||
|
|
@ -135,7 +149,7 @@ export default {
|
|||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.apiParams = this.$getApiParams('registerUserData')
|
||||
this.apiParams = this.$route.name === 'userdata' ? this.$getApiParams('registerUserData') : this.$getApiParams('registerCniConfiguration')
|
||||
},
|
||||
created () {
|
||||
this.initForm()
|
||||
|
|
@ -210,13 +224,23 @@ export default {
|
|||
params.account = values.account
|
||||
}
|
||||
|
||||
params.userdata = values.isbase64 ? values.userdata : this.$toBase64AndURIEncoded(values.userdata)
|
||||
let apiName = 'registerUserData'
|
||||
if (this.$route.name === 'cniconfiguration') {
|
||||
apiName = 'registerCniConfiguration'
|
||||
}
|
||||
|
||||
if (apiName === 'registerUserData') {
|
||||
params.userdata = values.isbase64 ? values.userdata : this.$toBase64AndURIEncoded(values.userdata)
|
||||
} else {
|
||||
params.cniconfig = values.isbase64 ? values.cniconfig : this.$toBase64AndURIEncoded(values.cniconfig)
|
||||
}
|
||||
console.log(params)
|
||||
if (values.params != null && values.params.length > 0) {
|
||||
var userdataparams = values.params.join(',')
|
||||
params.params = userdataparams
|
||||
}
|
||||
|
||||
api('registerUserData', {}, 'POST', params).then(json => {
|
||||
api(apiName, {}, 'POST', params).then(json => {
|
||||
this.$message.success(this.$t('message.success.register.user.data') + ' ' + values.name)
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
|
|
@ -226,7 +250,8 @@ export default {
|
|||
this.closeAction()
|
||||
})
|
||||
}).catch(error => {
|
||||
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||
console.log(error)
|
||||
this.formRef.value.scrollToField(error?.errorFields?.[0]?.name)
|
||||
})
|
||||
},
|
||||
downloadKey () {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-input-search
|
||||
v-if="showSearch"
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
:placeholder="$t('label.search')"
|
||||
v-model:value="filter"
|
||||
|
|
@ -34,6 +35,7 @@
|
|||
>
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'name'"><solution-outlined /> {{ $t('label.userdata') }}</template>
|
||||
<template v-if="column.key === 'AS_NUMBER'"><user-outlined /> {{ $t('label.account') }}</template>
|
||||
<template v-if="column.key === 'account'"><user-outlined /> {{ $t('label.account') }}</template>
|
||||
<template v-if="column.key === 'domain'"><block-outlined /> {{ $t('label.domain') }}</template>
|
||||
</template>
|
||||
|
|
@ -72,6 +74,10 @@ export default {
|
|||
zoneId: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
showSearch: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue