Skip to content

Commit 5f5c7dc

Browse files
sudo87dhslove
authored andcommitted
import network acl rules using csv (apache#12013)
1 parent 3de6635 commit 5f5c7dc

File tree

9 files changed

+699
-12
lines changed

9 files changed

+699
-12
lines changed

api/src/main/java/com/cloud/event/EventTypes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ public class EventTypes {
590590

591591
// Network ACL
592592
public static final String EVENT_NETWORK_ACL_CREATE = "NETWORK.ACL.CREATE";
593+
public static final String EVENT_NETWORK_ACL_IMPORT = "NETWORK.ACL.IMPORT";
593594
public static final String EVENT_NETWORK_ACL_DELETE = "NETWORK.ACL.DELETE";
594595
public static final String EVENT_NETWORK_ACL_REPLACE = "NETWORK.ACL.REPLACE";
595596
public static final String EVENT_NETWORK_ACL_UPDATE = "NETWORK.ACL.UPDATE";

api/src/main/java/com/cloud/network/vpc/NetworkACLService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.List;
2020

2121
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
22+
import org.apache.cloudstack.api.command.user.network.ImportNetworkACLCmd;
2223
import org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd;
2324
import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd;
2425
import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd;
@@ -98,4 +99,6 @@ public interface NetworkACLService {
9899
NetworkACLItem moveNetworkAclRuleToNewPosition(MoveNetworkAclItemCmd moveNetworkAclItemCmd);
99100

100101
NetworkACLItem moveRuleToTheTopInACLList(NetworkACLItem ruleBeingMoved);
102+
103+
List<NetworkACLItem> importNetworkACLRules(ImportNetworkACLCmd cmd) throws ResourceUnavailableException;
101104
}

api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd {
5858
private Integer publicEndPort;
5959

6060
@Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "The CIDR list to allow traffic from/to. Multiple entries must be separated by a single comma character (,).")
61-
private List<String> cidrlist;
61+
private List<String> cidrList;
6262

6363
@Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "Type of the ICMP message being sent")
6464
private Integer icmpType;
@@ -118,8 +118,8 @@ public void setProtocol(String protocol) {
118118
}
119119

120120
public List<String> getSourceCidrList() {
121-
if (cidrlist != null) {
122-
return cidrlist;
121+
if (cidrList != null) {
122+
return cidrList;
123123
} else {
124124
List<String> oneCidrList = new ArrayList<String>();
125125
oneCidrList.add(NetUtils.ALL_IP4_CIDRS);
@@ -238,6 +238,30 @@ public String getReason() {
238238
return reason;
239239
}
240240

241+
public void setCidrList(List<String> cidrList) {
242+
this.cidrList = cidrList;
243+
}
244+
245+
public void setIcmpType(Integer icmpType) {
246+
this.icmpType = icmpType;
247+
}
248+
249+
public void setIcmpCode(Integer icmpCode) {
250+
this.icmpCode = icmpCode;
251+
}
252+
253+
public void setNumber(Integer number) {
254+
this.number = number;
255+
}
256+
257+
public void setDisplay(Boolean display) {
258+
this.display = display;
259+
}
260+
261+
public void setReason(String reason) {
262+
this.reason = reason;
263+
}
264+
241265
@Override
242266
public void create() {
243267
NetworkACLItem result = _networkACLService.createNetworkACLItem(this);
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.user.network;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.Map;
22+
23+
import org.apache.cloudstack.api.APICommand;
24+
import org.apache.cloudstack.api.ApiConstants;
25+
import org.apache.cloudstack.api.ApiErrorCode;
26+
import org.apache.cloudstack.api.BaseAsyncCmd;
27+
import org.apache.cloudstack.api.Parameter;
28+
import org.apache.cloudstack.api.ServerApiException;
29+
import org.apache.cloudstack.api.response.ListResponse;
30+
import org.apache.cloudstack.api.response.NetworkACLItemResponse;
31+
import org.apache.cloudstack.api.response.NetworkACLResponse;
32+
import org.apache.cloudstack.context.CallContext;
33+
import org.apache.commons.collections.MapUtils;
34+
35+
import com.cloud.event.EventTypes;
36+
import com.cloud.exception.ResourceUnavailableException;
37+
import com.cloud.network.vpc.NetworkACLItem;
38+
import com.cloud.user.Account;
39+
40+
@APICommand(name = "importNetworkACL", description = "Imports Network ACL rules.",
41+
responseObject = NetworkACLItemResponse.class,
42+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
43+
since = "4.22.1")
44+
public class ImportNetworkACLCmd extends BaseAsyncCmd {
45+
46+
// ///////////////////////////////////////////////////
47+
// ////////////// API parameters /////////////////////
48+
// ///////////////////////////////////////////////////
49+
50+
@Parameter(
51+
name = ApiConstants.ACL_ID,
52+
type = CommandType.UUID,
53+
entityType = NetworkACLResponse.class,
54+
required = true,
55+
description = "The ID of the Network ACL to which the rules will be imported"
56+
)
57+
private Long aclId;
58+
59+
@Parameter(name = ApiConstants.RULES, type = CommandType.MAP, required = true,
60+
description = "Rules param list, id and protocol are must. Invalid rules will be discarded. Example: " +
61+
"rules[0].id=101&rules[0].protocol=tcp&rules[0].traffictype=ingress&rules[0].state=active&rules[0].cidrlist=192.168.1.0/24" +
62+
"&rules[0].tags=web&rules[0].aclid=acl-001&rules[0].aclname=web-acl&rules[0].number=1&rules[0].action=allow&rules[0].fordisplay=true" +
63+
"&rules[0].description=allow%20web%20traffic&rules[1].id=102&rules[1].protocol=udp&rules[1].traffictype=egress&rules[1].state=enabled" +
64+
"&rules[1].cidrlist=10.0.0.0/8&rules[1].tags=db&rules[1].aclid=acl-002&rules[1].aclname=db-acl&rules[1].number=2&rules[1].action=deny" +
65+
"&rules[1].fordisplay=false&rules[1].description=deny%20database%20traffic")
66+
private Map rules;
67+
68+
69+
// ///////////////////////////////////////////////////
70+
// ///////////////// Accessors ///////////////////////
71+
// ///////////////////////////////////////////////////
72+
73+
// Returns map, corresponds to a rule with the details in the keys:
74+
// id, protocol, startport, endport, traffictype, state, cidrlist, tags, aclid, aclname, number, action, fordisplay, description
75+
public Map getRules() {
76+
return rules;
77+
}
78+
79+
public Long getAclId() {
80+
return aclId;
81+
}
82+
83+
// ///////////////////////////////////////////////////
84+
// ///////////// API Implementation///////////////////
85+
// ///////////////////////////////////////////////////
86+
87+
88+
@Override
89+
public void execute() throws ResourceUnavailableException {
90+
validateParams();
91+
List<NetworkACLItem> importedRules = _networkACLService.importNetworkACLRules(this);
92+
ListResponse<NetworkACLItemResponse> response = new ListResponse<>();
93+
List<NetworkACLItemResponse> aclResponse = new ArrayList<>();
94+
for (NetworkACLItem acl : importedRules) {
95+
NetworkACLItemResponse ruleData = _responseGenerator.createNetworkACLItemResponse(acl);
96+
aclResponse.add(ruleData);
97+
}
98+
response.setResponses(aclResponse, importedRules.size());
99+
response.setResponseName(getCommandName());
100+
setResponseObject(response);
101+
}
102+
103+
@Override
104+
public long getEntityOwnerId() {
105+
Account account = CallContext.current().getCallingAccount();
106+
if (account != null) {
107+
return account.getId();
108+
}
109+
return Account.ACCOUNT_ID_SYSTEM;
110+
}
111+
112+
@Override
113+
public String getEventType() {
114+
return EventTypes.EVENT_NETWORK_ACL_IMPORT;
115+
}
116+
117+
@Override
118+
public String getEventDescription() {
119+
return "Importing ACL rules for ACL ID: " + getAclId();
120+
}
121+
122+
123+
private void validateParams() {
124+
if(MapUtils.isEmpty(rules)) {
125+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Rules parameter is empty or null");
126+
}
127+
128+
if (getAclId() == null || _networkACLService.getNetworkACL(getAclId()) == null) {
129+
throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to find Network ACL with provided ACL ID");
130+
}
131+
}
132+
}

server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java

Lines changed: 114 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,11 @@
2626

2727
import javax.inject.Inject;
2828

29-
import com.cloud.dc.DataCenter;
30-
import com.cloud.exception.PermissionDeniedException;
31-
import com.cloud.network.dao.NetrisProviderDao;
32-
import com.cloud.network.dao.NsxProviderDao;
33-
import com.cloud.network.element.NetrisProviderVO;
34-
import com.cloud.network.element.NsxProviderVO;
29+
import org.apache.cloudstack.api.ApiConstants;
3530
import org.apache.cloudstack.api.ApiErrorCode;
3631
import org.apache.cloudstack.api.ServerApiException;
3732
import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd;
33+
import org.apache.cloudstack.api.command.user.network.ImportNetworkACLCmd;
3834
import org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd;
3935
import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd;
4036
import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd;
@@ -43,19 +39,26 @@
4339
import org.apache.cloudstack.context.CallContext;
4440
import org.apache.commons.codec.digest.DigestUtils;
4541
import org.apache.commons.collections.CollectionUtils;
42+
import org.apache.commons.lang3.BooleanUtils;
4643
import org.apache.commons.lang3.ObjectUtils;
4744
import org.apache.commons.lang3.StringUtils;
4845
import org.springframework.stereotype.Component;
4946

47+
import com.cloud.dc.DataCenter;
5048
import com.cloud.event.ActionEvent;
5149
import com.cloud.event.EventTypes;
5250
import com.cloud.exception.InvalidParameterValueException;
51+
import com.cloud.exception.PermissionDeniedException;
5352
import com.cloud.exception.ResourceUnavailableException;
5453
import com.cloud.network.Network;
5554
import com.cloud.network.NetworkModel;
5655
import com.cloud.network.Networks;
56+
import com.cloud.network.dao.NetrisProviderDao;
5757
import com.cloud.network.dao.NetworkDao;
5858
import com.cloud.network.dao.NetworkVO;
59+
import com.cloud.network.dao.NsxProviderDao;
60+
import com.cloud.network.element.NetrisProviderVO;
61+
import com.cloud.network.element.NsxProviderVO;
5962
import com.cloud.network.vpc.NetworkACLItem.Action;
6063
import com.cloud.network.vpc.NetworkACLItem.TrafficType;
6164
import com.cloud.network.vpc.dao.NetworkACLDao;
@@ -1061,6 +1064,111 @@ public NetworkACLItem moveRuleToTheTopInACLList(NetworkACLItem ruleBeingMoved) {
10611064
return moveRuleToTheTop(ruleBeingMoved, allRules);
10621065
}
10631066

1067+
@Override
1068+
public List<NetworkACLItem> importNetworkACLRules(ImportNetworkACLCmd cmd) throws ResourceUnavailableException {
1069+
long aclId = cmd.getAclId();
1070+
Map<Object, Object> rules = cmd.getRules();
1071+
List<NetworkACLItem> createdRules = new ArrayList<>();
1072+
List<String> errors = new ArrayList<>();
1073+
for (Map.Entry<Object, Object> entry : rules.entrySet()) {
1074+
try {
1075+
Map<String, Object> ruleMap = (Map<String, Object>) entry.getValue();
1076+
NetworkACLItem item = createACLRuleFromMap(ruleMap, aclId);
1077+
createdRules.add(item);
1078+
} catch (Exception ex) {
1079+
String error = "Failed to import rule at index " + entry.getKey() + ": " + ex.getMessage();
1080+
errors.add(error);
1081+
logger.error(error, ex);
1082+
}
1083+
}
1084+
// no rules got imported
1085+
if (createdRules.isEmpty() && !errors.isEmpty()) {
1086+
logger.error("Failed to import any ACL rules. Errors: {}", String.join("; ", errors));
1087+
throw new CloudRuntimeException("Failed to import any ACL rules.");
1088+
}
1089+
1090+
// apply ACL to network
1091+
if (!createdRules.isEmpty()) {
1092+
applyNetworkACL(aclId);
1093+
}
1094+
return createdRules;
1095+
}
1096+
1097+
private NetworkACLItem createACLRuleFromMap(Map<String, Object> ruleMap, long aclId) {
1098+
String protocol = (String) ruleMap.get(ApiConstants.PROTOCOL);
1099+
if (protocol == null || protocol.trim().isEmpty()) {
1100+
throw new InvalidParameterValueException("Protocol is required");
1101+
}
1102+
String action = (String) ruleMap.getOrDefault(ApiConstants.ACTION, "deny");
1103+
String trafficType = (String) ruleMap.getOrDefault(ApiConstants.TRAFFIC_TYPE, NetworkACLItem.TrafficType.Ingress);
1104+
String forDisplay = (String) ruleMap.getOrDefault(ApiConstants.FOR_DISPLAY, "true");
1105+
1106+
// Create ACL rule using the service
1107+
CreateNetworkACLCmd cmd = new CreateNetworkACLCmd();
1108+
cmd.setAclId(aclId);
1109+
cmd.setProtocol(protocol.toLowerCase());
1110+
cmd.setAction(action.toLowerCase());
1111+
cmd.setTrafficType(trafficType.toLowerCase());
1112+
cmd.setDisplay(BooleanUtils.toBoolean(forDisplay));
1113+
1114+
// Optional parameters
1115+
if (ruleMap.containsKey(ApiConstants.CIDR_LIST)) {
1116+
Object cidrObj = ruleMap.get(ApiConstants.CIDR_LIST);
1117+
List<String> cidrList = new ArrayList<>();
1118+
if (cidrObj instanceof String) {
1119+
for (String cidr : ((String) cidrObj).split(",")) {
1120+
cidrList.add(cidr.trim());
1121+
}
1122+
} else if (cidrObj instanceof List) {
1123+
cidrList.addAll((List<String>) cidrObj);
1124+
}
1125+
cmd.setCidrList(cidrList);
1126+
}
1127+
1128+
if (ruleMap.containsKey(ApiConstants.START_PORT)) {
1129+
cmd.setPublicStartPort(parseInt(ruleMap.get(ApiConstants.START_PORT)));
1130+
}
1131+
1132+
if (ruleMap.containsKey(ApiConstants.END_PORT)) {
1133+
cmd.setPublicEndPort(parseInt(ruleMap.get(ApiConstants.END_PORT)));
1134+
}
1135+
1136+
if (ruleMap.containsKey(ApiConstants.NUMBER)) {
1137+
cmd.setNumber(parseInt(ruleMap.get(ApiConstants.NUMBER)));
1138+
}
1139+
1140+
if (ruleMap.containsKey(ApiConstants.ICMP_TYPE)) {
1141+
cmd.setIcmpType(parseInt(ruleMap.get(ApiConstants.ICMP_TYPE)));
1142+
}
1143+
1144+
if (ruleMap.containsKey(ApiConstants.ICMP_CODE)) {
1145+
cmd.setIcmpCode(parseInt(ruleMap.get(ApiConstants.ICMP_CODE)));
1146+
}
1147+
1148+
if (ruleMap.containsKey(ApiConstants.ACL_REASON)) {
1149+
cmd.setReason((String) ruleMap.get(ApiConstants.ACL_REASON));
1150+
}
1151+
1152+
return createNetworkACLItem(cmd);
1153+
}
1154+
1155+
private Integer parseInt(Object value) {
1156+
if (value == null) {
1157+
return null;
1158+
}
1159+
if (value instanceof Integer) {
1160+
return (Integer) value;
1161+
}
1162+
if (value instanceof String) {
1163+
try {
1164+
return Integer.parseInt((String) value);
1165+
} catch (NumberFormatException e) {
1166+
throw new InvalidParameterValueException("Invalid integer value: " + value);
1167+
}
1168+
}
1169+
throw new InvalidParameterValueException("Cannot convert to integer: " + value);
1170+
}
1171+
10641172
/**
10651173
* Validates the consistency of the ACL; the validation process is the following.
10661174
* <ul>

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@
491491
import org.apache.cloudstack.api.command.user.network.DeleteNetworkACLCmd;
492492
import org.apache.cloudstack.api.command.user.network.DeleteNetworkACLListCmd;
493493
import org.apache.cloudstack.api.command.user.network.DeleteNetworkCmd;
494+
import org.apache.cloudstack.api.command.user.network.ImportNetworkACLCmd;
494495
import org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd;
495496
import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd;
496497
import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd;
@@ -6738,6 +6739,7 @@ public List<Class<?>> getCommands() {
67386739
cmdList.add(EnableStaticNatCmd.class);
67396740
cmdList.add(ListIpForwardingRulesCmd.class);
67406741
cmdList.add(CreateNetworkACLCmd.class);
6742+
cmdList.add(ImportNetworkACLCmd.class);
67416743
cmdList.add(CreateNetworkCmd.class);
67426744
cmdList.add(DeleteNetworkACLCmd.class);
67436745
cmdList.add(DeleteNetworkCmd.class);

0 commit comments

Comments
 (0)