diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index bda3326cb51..318ac192252 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -116,6 +116,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Provider VirtualRouter = new Provider("VirtualRouter", false); public static final Provider JuniperContrail = new Provider("JuniperContrail", false); public static final Provider JuniperSRX = new Provider("JuniperSRX", true); + public static final Provider PaloAlto = new Provider("PaloAlto", true); public static final Provider F5BigIp = new Provider("F5BigIp", true); public static final Provider Netscaler = new Provider("Netscaler", true); public static final Provider ExternalDhcpServer = new Provider("ExternalDhcpServer", true); diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java index 4983255389d..a7906f41522 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java @@ -47,8 +47,9 @@ public class AddNetworkDeviceCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// + @Inject ExternalNetworkDeviceManager nwDeviceMgr; - @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall") + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall, PaloAltoFirewall") private String type; @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java index 0b7836de3a8..5278ba9efd7 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java @@ -51,7 +51,7 @@ public class ListNetworkDeviceCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall") + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall, PaloAltoFirewall") private String type; @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java index 29ce2e3971d..32f13f80f22 100644 --- a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java +++ b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java @@ -42,6 +42,7 @@ public interface ExternalNetworkDeviceManager extends Manager { public static final NetworkDevice NetscalerSDXLoadBalancer = new NetworkDevice("NetscalerSDXLoadBalancer", Network.Provider.Netscaler.getName()); public static final NetworkDevice F5BigIpLoadBalancer = new NetworkDevice("F5BigIpLoadBalancer", Network.Provider.F5BigIp.getName()); public static final NetworkDevice JuniperSRXFirewall = new NetworkDevice("JuniperSRXFirewall", Network.Provider.JuniperSRX.getName()); + public static final NetworkDevice PaloAltoFirewall = new NetworkDevice("PaloAltoFirewall", Network.Provider.PaloAlto.getName()); public static final NetworkDevice NiciraNvp = new NetworkDevice("NiciraNvp", Network.Provider.NiciraNvp.getName()); public static final NetworkDevice CiscoVnmc = new NetworkDevice("CiscoVnmc", Network.Provider.CiscoVnmc.getName()); diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index d548527267b..e450c29a0c5 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -304,6 +304,7 @@ label.add.new.F5=Add new F5 label.add.new.gateway=Add new gateway label.add.new.NetScaler=Add new NetScaler label.add.new.SRX=Add new SRX +label.add.new.PA=Add new Palo Alto label.add.new.tier=Add new tier label.add.NiciraNvp.device=Add Nvp Controller label.add.physical.network=Add physical network @@ -318,6 +319,7 @@ label.add.secondary.storage=Add Secondary Storage label.add.security.group=Add Security Group label.add.service.offering=Add Service Offering label.add.SRX.device=Add SRX device +label.add.PA.device=Add Palo Alto device label.add.static.nat.rule=Add static NAT rule label.add.static.route=Add static route label.add.system.service.offering=Add System Service Offering @@ -479,6 +481,7 @@ label.delete.NetScaler=Delete NetScaler label.delete.NiciraNvp=Remove Nvp Controller label.delete.project=Delete project label.delete.SRX=Delete SRX +label.delete.PA=Delete Palo Alto label.delete.VPN.connection=delete VPN connection label.delete.VPN.customer.gateway=delete VPN Customer Gateway label.delete.VPN.gateway=delete VPN Gateway @@ -876,6 +879,8 @@ label.os.type=OS Type label.owned.public.ips=Owned Public IP Addresses label.owner.account=Owner Account label.owner.domain=Owner Domain +label.PA.log.profile=Palo Alto Log Profile +label.PA.threat.profile=Palo Alto Threat Profile label.parent.domain=Parent Domain label.password.enabled=Password Enabled label.password=Password @@ -1048,6 +1053,7 @@ label.specify.vlan=Specify VLAN label.specify.vxlan=Specify VXLAN label.SR.name = SR Name-Label label.srx=SRX +label.PA=Palo Alto label.start.IP=Start IP label.start.port=Start Port label.start.reserved.system.IP=Start Reserved system IP @@ -1366,6 +1372,7 @@ message.confirm.action.force.reconnect=Please confirm that you want to force rec message.confirm.delete.F5=Please confirm that you would like to delete F5 message.confirm.delete.NetScaler=Please confirm that you would like to delete NetScaler message.confirm.delete.SRX=Please confirm that you would like to delete SRX +message.confirm.delete.PA=Please confirm that you would like to delete Palo Alto message.confirm.destroy.router=Please confirm that you would like to destroy this router message.confirm.disable.provider=Please confirm that you would like to disable this provider message.confirm.enable.provider=Please confirm that you would like to enable this provider diff --git a/client/pom.xml b/client/pom.xml index 54cb667514b..a15a409a33c 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -90,6 +90,11 @@ cloud-plugin-network-contrail ${project.version} + + org.apache.cloudstack + cloud-plugin-network-palo-alto + ${project.version} + org.apache.cloudstack cloud-plugin-network-ovs diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 8cbe972cb81..087d8b9490b 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -533,6 +533,17 @@ configureSrxFirewall=1 listSrxFirewalls=1 listSrxFirewallNetworks=1 +#### Palo Alto firewall commands +addExternalFirewall=1 +deleteExternalFirewall=1 +listExternalFirewalls=1 + +addPaloAltoFirewall=1 +deletePaloAltoFirewall=1 +configurePaloAltoFirewall=1 +listPaloAltoFirewalls=1 +listPaloAltoFirewallNetworks=1 + ####Netapp integration commands createVolumeOnFiler=15 destroyVolumeOnFiler=15 diff --git a/plugins/network-elements/palo-alto/pom.xml b/plugins/network-elements/palo-alto/pom.xml new file mode 100644 index 00000000000..50b4c134f60 --- /dev/null +++ b/plugins/network-elements/palo-alto/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + cloud-plugin-network-palo-alto + Apache CloudStack Plugin - Palo Alto + + org.apache.cloudstack + cloudstack-plugins + 4.3.0-SNAPSHOT + ../../pom.xml + + diff --git a/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/module.properties b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/module.properties new file mode 100644 index 00000000000..960fdba8352 --- /dev/null +++ b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/module.properties @@ -0,0 +1,18 @@ +# 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. +name=paloalto +parent=network \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/spring-paloalto-context.xml b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/spring-paloalto-context.xml new file mode 100644 index 00000000000..251f444b8a0 --- /dev/null +++ b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/spring-paloalto-context.xml @@ -0,0 +1,33 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddExternalFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddExternalFirewallCmd.java new file mode 100644 index 00000000000..84ee869866a --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddExternalFirewallCmd.java @@ -0,0 +1,112 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.api.response.ExternalFirewallResponse; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "addExternalFirewall", description="Adds an external firewall appliance", responseObject = ExternalFirewallResponse.class) +public class AddExternalFirewallCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddExternalFirewallCmd.class.getName()); + private static final String s_name = "addexternalfirewallresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, + required = true, description="Zone in which to add the external firewall appliance.") + private Long zoneId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external firewall appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Username of the external firewall appliance.") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Password of the external firewall appliance.") + private String password; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Inject PaloAltoFirewallElementService _paElementService; + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(){ + try { + Host externalFirewall = _paElementService.addExternalFirewall(this); + ExternalFirewallResponse response = _paElementService.createExternalFirewallResponse(externalFirewall); + response.setObjectName("externalfirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, cre.getMessage()); + } + } +} + diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddPaloAltoFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddPaloAltoFirewallCmd.java new file mode 100644 index 00000000000..faf28e2cf26 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddPaloAltoFirewallCmd.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.context.CallContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "addPaloAltoFirewall", responseObject=PaloAltoFirewallResponse.class, description="Adds a Palo Alto firewall device") +public class AddPaloAltoFirewallCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddPaloAltoFirewallCmd.class.getName()); + private static final String s_name = "addpaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.UUID, entityType = PhysicalNetworkResponse.class, + required=true, description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the Palo Alto appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Credentials to reach Palo Alto firewall device") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Credentials to reach Palo Alto firewall device") + private String password; + + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, required = true, description = "supports only PaloAltoFirewall") + private String deviceType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getDeviceType() { + return deviceType; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + ExternalFirewallDeviceVO fwDeviceVO = _paFwService.addPaloAltoFirewall(this); + if (fwDeviceVO != null) { + PaloAltoFirewallResponse response = _paFwService.createPaloAltoFirewallResponse(fwDeviceVO); + response.setObjectName("pafirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Palo Alto firewall due to internal error."); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getEventDescription() { + return "Adding a Palo Alto firewall device"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_FIREWALL_DEVICE_ADD; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ConfigurePaloAltoFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ConfigurePaloAltoFirewallCmd.java new file mode 100644 index 00000000000..199bb832d7d --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ConfigurePaloAltoFirewallCmd.java @@ -0,0 +1,114 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.context.CallContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "configurePaloAltoFirewall", responseObject=PaloAltoFirewallResponse.class, description="Configures a Palo Alto firewall device") +public class ConfigurePaloAltoFirewallCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(ConfigurePaloAltoFirewallCmd.class.getName()); + private static final String s_name = "configurepaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + required=true, description="Palo Alto firewall device ID") + private Long fwDeviceId; + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_CAPACITY, type=CommandType.LONG, required=false, description="capacity of the firewall device, Capacity will be interpreted as number of networks device can handle") + private Long capacity; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + public Long getFirewallCapacity() { + return capacity; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + ExternalFirewallDeviceVO fwDeviceVO = _paFwService.configurePaloAltoFirewall(this); + if (fwDeviceVO != null) { + PaloAltoFirewallResponse response = _paFwService.createPaloAltoFirewallResponse(fwDeviceVO); + response.setObjectName("pafirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure Palo Alto firewall device due to internal error."); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getEventDescription() { + return "Configuring a Palo Alto firewall device"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_FIREWALL_DEVICE_CONFIGURE; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java new file mode 100644 index 00000000000..93f752ac47c --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.element.PaloAltoFirewallElementService; +import com.cloud.user.Account; + +@APICommand(name = "deleteExternalFirewall", description="Deletes an external firewall appliance.", responseObject = SuccessResponse.class) +public class DeleteExternalFirewallCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteExternalFirewallCmd.class.getName()); + private static final String s_name = "deleteexternalfirewallresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = HostResponse.class, + required = true, description="Id of the external firewall appliance.") + private Long id; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Inject PaloAltoFirewallElementService _paElementService; + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(){ + try { + boolean result = _paElementService.deleteExternalFirewall(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete external firewall."); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Failed to delete external firewall."); + } + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeletePaloAltoFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeletePaloAltoFirewallCmd.java new file mode 100644 index 00000000000..86149816af5 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeletePaloAltoFirewallCmd.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.context.CallContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deletePaloAltoFirewall", responseObject=SuccessResponse.class, description=" delete a Palo Alto firewall device") +public class DeletePaloAltoFirewallCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeletePaloAltoFirewallCmd.class.getName()); + private static final String s_name = "deletepaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paElementService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + required=true, description="Palo Alto firewall device ID") + private Long fwDeviceId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + boolean result = _paElementService.deletePaloAltoFirewall(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Palo Alto firewall device"); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getEventDescription() { + return "Deleting Palo Alto firewall device"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_FIREWALL_DEVICE_DELETE; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListExternalFirewallsCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListExternalFirewallsCmd.java new file mode 100644 index 00000000000..ebced7e2039 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListExternalFirewallsCmd.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import com.cloud.host.Host; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.api.response.ExternalFirewallResponse; + +@APICommand(name = "listExternalFirewalls", description="List external firewall appliances.", responseObject = ExternalFirewallResponse.class) +public class ListExternalFirewallsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListServiceOfferingsCmd.class.getName()); + private static final String s_name = "listexternalfirewallsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, + required = true, description="zone Id") + private long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Inject PaloAltoFirewallElementService _paElementService; + + @Override + public String getCommandName() { + return s_name; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(){ + + List externalFirewalls = _paElementService.listExternalFirewalls(this); + + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (Host externalFirewall : externalFirewalls) { + ExternalFirewallResponse response = _paElementService.createExternalFirewallResponse(externalFirewall); + response.setObjectName("externalfirewall"); + response.setResponseName(getCommandName()); + responses.add(response); + } + + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallNetworksCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallNetworksCmd.java new file mode 100644 index 00000000000..15c5bfcdbed --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallNetworksCmd.java @@ -0,0 +1,95 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.*; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.element.PaloAltoFirewallElementService; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "listPaloAltoFirewallNetworks", responseObject=NetworkResponse.class, description="lists network that are using Palo Alto firewall device") +public class ListPaloAltoFirewallNetworksCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(ListPaloAltoFirewallNetworksCmd.class.getName()); + private static final String s_name = "listpaloaltofirewallnetworksresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.LOAD_BALANCER_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + required = true, description="palo alto balancer device ID") + private Long fwDeviceId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + List networks = _paFwService.listNetworks(this); + ListResponse response = new ListResponse(); + List networkResponses = new ArrayList(); + + if (networks != null && !networks.isEmpty()) { + for (Network network : networks) { + NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(network); + networkResponses.add(networkResponse); + } + } + + response.setResponses(networkResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallsCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallsCmd.java new file mode 100644 index 00000000000..b788aca8c35 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallsCmd.java @@ -0,0 +1,103 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.ListResponse; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.element.PaloAltoFirewallElementService; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "listPaloAltoFirewalls", responseObject=PaloAltoFirewallResponse.class, description="lists Palo Alto firewall devices in a physical network") +public class ListPaloAltoFirewallsCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(ListPaloAltoFirewallsCmd.class.getName()); + private static final String s_name = "listpaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.UUID, entityType = PhysicalNetworkResponse.class, + description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + description="Palo Alto firewall device ID") + private Long fwDeviceId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + List fwDevices = _paFwService.listPaloAltoFirewalls(this); + ListResponse response = new ListResponse(); + List fwDevicesResponse = new ArrayList(); + + if (fwDevices != null && !fwDevices.isEmpty()) { + for (ExternalFirewallDeviceVO fwDeviceVO : fwDevices) { + PaloAltoFirewallResponse deviceResponse = _paFwService.createPaloAltoFirewallResponse(fwDeviceVO); + fwDevicesResponse.add(deviceResponse); + } + } + + response.setResponses(fwDevicesResponse); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/response/PaloAltoFirewallResponse.java b/plugins/network-elements/palo-alto/src/com/cloud/api/response/PaloAltoFirewallResponse.java new file mode 100644 index 00000000000..cda018d7b4c --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/response/PaloAltoFirewallResponse.java @@ -0,0 +1,142 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.response; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.network.dao.ExternalFirewallDeviceVO; + +@EntityReference(value=ExternalFirewallDeviceVO.class) +@SuppressWarnings("unused") +public class PaloAltoFirewallResponse extends BaseResponse { + + @SerializedName(ApiConstants.FIREWALL_DEVICE_ID) @Param(description="device id of the Palo Alto firewall") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network to which this Palo Alto firewall belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PROVIDER) @Param(description="name of the provider") + private String providerName; + + @SerializedName(ApiConstants.FIREWALL_DEVICE_NAME) @Param(description="device name") + private String deviceName; + + @SerializedName(ApiConstants.FIREWALL_DEVICE_STATE) @Param(description="device state") + private String deviceState; + + @SerializedName(ApiConstants.FIREWALL_DEVICE_CAPACITY) @Param(description="device capacity") + private Long deviceCapacity; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID of the external firewall") + private String zoneId; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the management IP address of the external firewall") + private String ipAddress; + + @SerializedName(ApiConstants.USERNAME) @Param(description="the username that's used to log in to the external firewall") + private String username; + + @SerializedName(ApiConstants.PUBLIC_INTERFACE) @Param(description="the public interface of the external firewall") + private String publicInterface; + + @SerializedName(ApiConstants.USAGE_INTERFACE) @Param(description="the usage interface of the external firewall") + private String usageInterface; + + @SerializedName(ApiConstants.PRIVATE_INTERFACE) @Param(description="the private interface of the external firewall") + private String privateInterface; + + @SerializedName(ApiConstants.PUBLIC_ZONE) @Param(description="the public security zone of the external firewall") + private String publicZone; + + @SerializedName(ApiConstants.PRIVATE_ZONE) @Param(description="the private security zone of the external firewall") + private String privateZone; + + @SerializedName(ApiConstants.NUM_RETRIES) @Param(description="the number of times to retry requests to the external firewall") + private String numRetries; + + @SerializedName(ApiConstants.TIMEOUT) @Param(description="the timeout (in seconds) for requests to the external firewall") + private String timeout; + + public void setId(String lbDeviceId) { + this.id = lbDeviceId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public void setProvider(String provider) { + this.providerName = provider; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public void setDeviceCapacity(long deviceCapacity) { + this.deviceCapacity = deviceCapacity; + } + + public void setDeviceState(String deviceState) { + this.deviceState = deviceState; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setPublicInterface(String publicInterface) { + this.publicInterface = publicInterface; + } + + public void setUsageInterface(String usageInterface) { + this.usageInterface = usageInterface; + } + + public void setPrivateInterface(String privateInterface) { + this.privateInterface = privateInterface; + } + + public void setPublicZone(String publicZone) { + this.publicZone = publicZone; + } + + public void setPrivateZone(String privateZone) { + this.privateZone = privateZone; + } + + public String getNumRetries() { + return numRetries; + } + + public void setNumRetries(String numRetries) { + this.numRetries = numRetries; + } + + public String getTimeout() { + return timeout; + } + + public void setTimeout(String timeout) { + this.timeout = timeout; + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoExternalFirewallElement.java b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoExternalFirewallElement.java new file mode 100644 index 00000000000..3eb802ed8f6 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoExternalFirewallElement.java @@ -0,0 +1,538 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.element; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.ExternalFirewallResponse; +import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice; +import org.apache.log4j.Logger; + +import com.cloud.api.ApiDBUtils; +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.AddPaloAltoFirewallCmd; +import com.cloud.api.commands.ConfigurePaloAltoFirewallCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.DeletePaloAltoFirewallCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.api.commands.ListPaloAltoFirewallNetworksCmd; +import com.cloud.api.commands.ListPaloAltoFirewallsCmd; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.ExternalFirewallDeviceManagerImpl; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkModel; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.dao.ExternalFirewallDeviceDao; +import com.cloud.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkExternalFirewallDao; +import com.cloud.network.dao.NetworkExternalFirewallVO; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.ExternalFirewallDeviceVO.FirewallDeviceState; +import com.cloud.network.resource.PaloAltoResource; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = {NetworkElement.class, FirewallServiceProvider.class, + PortForwardingServiceProvider.class, IpDeployer.class, + SourceNatServiceProvider.class}) +public class PaloAltoExternalFirewallElement extends ExternalFirewallDeviceManagerImpl implements SourceNatServiceProvider, FirewallServiceProvider, +PortForwardingServiceProvider, IpDeployer, PaloAltoFirewallElementService, StaticNatServiceProvider { + + private static final Logger s_logger = Logger.getLogger(PaloAltoExternalFirewallElement.class); + + private static final Map> capabilities = setCapabilities(); + + @Inject + NetworkModel _networkManager; + @Inject + HostDao _hostDao; + @Inject + ConfigurationManager _configMgr; + @Inject + NetworkOfferingDao _networkOfferingDao; + @Inject + NetworkDao _networksDao; + @Inject + DataCenterDao _dcDao; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + ExternalFirewallDeviceDao _fwDevicesDao; + @Inject + NetworkExternalFirewallDao _networkFirewallDao; + @Inject + NetworkDao _networkDao; + @Inject + NetworkServiceMapDao _ntwkSrvcDao; + @Inject + HostDetailsDao _hostDetailDao; + @Inject + ConfigurationDao _configDao; + @Inject + EntityManager _entityMgr; + + private boolean canHandle(Network network, Service service) { + DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + if (zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() != Network.GuestType.Isolated) { + s_logger.trace("Element " + getProvider().getName() + "is not handling network type = " + network.getGuestType()); + return false; + } + + if (service == null) { + if (!_networkManager.isProviderForNetwork(getProvider(), network.getId())) { + s_logger.trace("Element " + getProvider().getName() + " is not a provider for the network " + network); + return false; + } + } else { + if (!_networkManager.isProviderSupportServiceInNetwork(network.getId(), service, getProvider())) { + s_logger.trace("Element " + getProvider().getName() + " doesn't support service " + service.getName() + " in the network " + network); + return false; + } + } + + return true; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, + InsufficientNetworkCapacityException { + DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + + // don't have to implement network is Basic zone + if (zone.getNetworkType() == NetworkType.Basic) { + s_logger.debug("Not handling network implement in zone of type " + NetworkType.Basic); + return false; + } + + if (!canHandle(network, null)) { + return false; + } + + try { + return manageGuestNetworkWithExternalFirewall(true, network); + } catch (InsufficientCapacityException capacityException) { + // TODO: handle out of capacity exception in more gracefule manner when multiple providers are present for + // the network + s_logger.error("Fail to implement the Palo Alto for network " + network, capacityException); + return false; + } + } + + @Override + public boolean prepare(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, + InsufficientNetworkCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean release(Network config, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ResourceUnavailableException, ConcurrentOperationException { + DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + + // don't have to implement network is Basic zone + if (zone.getNetworkType() == NetworkType.Basic) { + s_logger.debug("Not handling network shutdown in zone of type " + NetworkType.Basic); + return false; + } + + if (!canHandle(network, null)) { + return false; + } + try { + return manageGuestNetworkWithExternalFirewall(false, network); + } catch (InsufficientCapacityException capacityException) { + // TODO: handle out of capacity exception + return false; + } + } + + @Override + public boolean destroy(Network config, ReservationContext context) { + return true; + } + + @Override + public boolean applyFWRules(Network config, List rules) throws ResourceUnavailableException { + if (!canHandle(config, Service.Firewall)) { + return false; + } + + return applyFirewallRules(config, rules); + } + + @Override + public Provider getProvider() { + return Provider.PaloAlto; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap>(); + + // Set capabilities for Firewall service + Map firewallCapabilities = new HashMap(); + firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); + firewallCapabilities.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp,all"); + firewallCapabilities.put(Capability.MultipleIps, "true"); + firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); + firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress, egress"); + capabilities.put(Service.Firewall, firewallCapabilities); + + capabilities.put(Service.Gateway, null); + + Map sourceNatCapabilities = new HashMap(); + // Specifies that this element supports either one source NAT rule per account; + sourceNatCapabilities.put(Capability.SupportedSourceNatTypes, "peraccount"); + capabilities.put(Service.SourceNat, sourceNatCapabilities); + + // Specifies that port forwarding rules are supported by this element + capabilities.put(Service.PortForwarding, null); + + // Specifies that static NAT rules are supported by this element + capabilities.put(Service.StaticNat, null); + + return capabilities; + } + + @Override + public boolean applyPFRules(Network network, List rules) throws ResourceUnavailableException { + if (!canHandle(network, Service.PortForwarding)) { + return false; + } + + return applyPortForwardingRules(network, rules); + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + + List fwDevices = _fwDevicesDao.listByPhysicalNetworkAndProvider(provider.getPhysicalNetworkId(), Provider.PaloAlto.getName()); + // true if at-least one Palo Alto device is added in to physical network and is in configured (in enabled state) state + if (fwDevices != null && !fwDevices.isEmpty()) { + for (ExternalFirewallDeviceVO fwDevice : fwDevices) { + if (fwDevice.getDeviceState() == FirewallDeviceState.Enabled) { + return true; + } + } + } + return false; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return true; + } + + @Override + @Deprecated + // should use more generic addNetworkDevice command to add firewall + public Host addExternalFirewall(AddExternalFirewallCmd cmd) { + Long zoneId = cmd.getZoneId(); + DataCenterVO zone = null; + PhysicalNetworkVO pNetwork = null; + HostVO fwHost = null; + + zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } + + List physicalNetworks = _physicalNetworkDao.listByZone(zoneId); + if ((physicalNetworks == null) || (physicalNetworks.size() > 1)) { + throw new InvalidParameterValueException("There are no physical networks or multiple physical networks configured in zone with ID: " + + zoneId + " to add this device."); + } + pNetwork = physicalNetworks.get(0); + + String deviceType = NetworkDevice.PaloAltoFirewall.getName(); + ExternalFirewallDeviceVO fwDeviceVO = addExternalFirewall(pNetwork.getId(), cmd.getUrl(), cmd.getUsername(), cmd.getPassword(), deviceType, new PaloAltoResource()); + if (fwDeviceVO != null) { + fwHost = _hostDao.findById(fwDeviceVO.getHostId()); + } + + return fwHost; + } + + @Override + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd) { + return deleteExternalFirewall(cmd.getId()); + } + + @Override + @Deprecated + // should use more generic listNetworkDevice command + public List listExternalFirewalls(ListExternalFirewallsCmd cmd) { + List firewallHosts = new ArrayList(); + Long zoneId = cmd.getZoneId(); + DataCenterVO zone = null; + PhysicalNetworkVO pNetwork = null; + + if (zoneId != null) { + zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } + + List physicalNetworks = _physicalNetworkDao.listByZone(zoneId); + if ((physicalNetworks == null) || (physicalNetworks.size() > 1)) { + throw new InvalidParameterValueException("There are no physical networks or multiple physical networks configured in zone with ID: " + + zoneId + " to add this device."); + } + pNetwork = physicalNetworks.get(0); + } + + firewallHosts.addAll(listExternalFirewalls(pNetwork.getId(), NetworkDevice.PaloAltoFirewall.getName())); + return firewallHosts; + } + + @Override + public ExternalFirewallResponse createExternalFirewallResponse(Host externalFirewall) { + return super.createExternalFirewallResponse(externalFirewall); + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(AddExternalFirewallCmd.class); + cmdList.add(AddPaloAltoFirewallCmd.class); + cmdList.add(ConfigurePaloAltoFirewallCmd.class); + cmdList.add(DeleteExternalFirewallCmd.class); + cmdList.add(DeletePaloAltoFirewallCmd.class); + cmdList.add(ListExternalFirewallsCmd.class); + cmdList.add(ListPaloAltoFirewallNetworksCmd.class); + cmdList.add(ListPaloAltoFirewallsCmd.class); + return cmdList; + } + + @Override + public ExternalFirewallDeviceVO addPaloAltoFirewall(AddPaloAltoFirewallCmd cmd) { + String deviceName = cmd.getDeviceType(); + if (!deviceName.equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("Invalid Palo Alto firewall device type"); + } + return addExternalFirewall(cmd.getPhysicalNetworkId(), cmd.getUrl(), cmd.getUsername(), cmd.getPassword(), deviceName, + new PaloAltoResource()); + } + + @Override + public boolean deletePaloAltoFirewall(DeletePaloAltoFirewallCmd cmd) { + Long fwDeviceId = cmd.getFirewallDeviceId(); + + ExternalFirewallDeviceVO fwDeviceVO = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVO == null || !fwDeviceVO.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("No Palo Alto firewall device found with ID: " + fwDeviceId); + } + return deleteExternalFirewall(fwDeviceVO.getHostId()); + } + + @Override + public ExternalFirewallDeviceVO configurePaloAltoFirewall(ConfigurePaloAltoFirewallCmd cmd) { + Long fwDeviceId = cmd.getFirewallDeviceId(); + Long deviceCapacity = cmd.getFirewallCapacity(); + + ExternalFirewallDeviceVO fwDeviceVO = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVO == null || !fwDeviceVO.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("No Palo Alto firewall device found with ID: " + fwDeviceId); + } + + if (deviceCapacity != null) { + // check if any networks are using this Palo Alto device + List networks = _networkFirewallDao.listByFirewallDeviceId(fwDeviceId); + if ((networks != null) && !networks.isEmpty()) { + if (deviceCapacity < networks.size()) { + throw new CloudRuntimeException("There are more number of networks already using this Palo Alto firewall device than configured capacity"); + } + } + if (deviceCapacity != null) { + fwDeviceVO.setCapacity(deviceCapacity); + } + } + + fwDeviceVO.setDeviceState(FirewallDeviceState.Enabled); + _fwDevicesDao.update(fwDeviceId, fwDeviceVO); + return fwDeviceVO; + } + + @Override + public List listPaloAltoFirewalls(ListPaloAltoFirewallsCmd cmd) { + Long physcialNetworkId = cmd.getPhysicalNetworkId(); + Long fwDeviceId = cmd.getFirewallDeviceId(); + PhysicalNetworkVO pNetwork = null; + List fwDevices = new ArrayList(); + + if (physcialNetworkId == null && fwDeviceId == null) { + throw new InvalidParameterValueException("Either physical network Id or load balancer device Id must be specified"); + } + + if (fwDeviceId != null) { + ExternalFirewallDeviceVO fwDeviceVo = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVo == null || !fwDeviceVo.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("Could not find Palo Alto firewall device with ID: " + fwDeviceId); + } + fwDevices.add(fwDeviceVo); + } + + if (physcialNetworkId != null) { + pNetwork = _physicalNetworkDao.findById(physcialNetworkId); + if (pNetwork == null) { + throw new InvalidParameterValueException("Could not find phyical network with ID: " + physcialNetworkId); + } + fwDevices = _fwDevicesDao.listByPhysicalNetworkAndProvider(physcialNetworkId, Provider.PaloAlto.getName()); + } + + return fwDevices; + } + + @Override + public List listNetworks(ListPaloAltoFirewallNetworksCmd cmd) { + Long fwDeviceId = cmd.getFirewallDeviceId(); + List networks = new ArrayList(); + + ExternalFirewallDeviceVO fwDeviceVo = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVo == null || !fwDeviceVo.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("Could not find Palo Alto firewall device with ID " + fwDeviceId); + } + + List networkFirewallMaps = _networkFirewallDao.listByFirewallDeviceId(fwDeviceId); + if (networkFirewallMaps != null && !networkFirewallMaps.isEmpty()) { + for (NetworkExternalFirewallVO networkFirewallMap : networkFirewallMaps) { + NetworkVO network = _networkDao.findById(networkFirewallMap.getNetworkId()); + networks.add(network); + } + } + + return networks; + } + + @Override + public PaloAltoFirewallResponse createPaloAltoFirewallResponse(ExternalFirewallDeviceVO fwDeviceVO) { + PaloAltoFirewallResponse response = new PaloAltoFirewallResponse(); + Map fwDetails = _hostDetailDao.findDetails(fwDeviceVO.getHostId()); + Host fwHost = _hostDao.findById(fwDeviceVO.getHostId()); + + response.setId(fwDeviceVO.getUuid()); + PhysicalNetwork pnw = ApiDBUtils.findPhysicalNetworkById(fwDeviceVO.getPhysicalNetworkId()); + if (pnw != null) { + response.setPhysicalNetworkId(pnw.getUuid()); + } + response.setDeviceName(fwDeviceVO.getDeviceName()); + if (fwDeviceVO.getCapacity() == 0) { + long defaultFwCapacity = NumbersUtil.parseLong(_configDao.getValue(Config.DefaultExternalFirewallCapacity.key()), 50); + response.setDeviceCapacity(defaultFwCapacity); + } else { + response.setDeviceCapacity(fwDeviceVO.getCapacity()); + } + response.setProvider(fwDeviceVO.getProviderName()); + response.setDeviceState(fwDeviceVO.getDeviceState().name()); + response.setIpAddress(fwHost.getPrivateIpAddress()); + response.setPublicInterface(fwDetails.get("publicInterface")); + response.setUsageInterface(fwDetails.get("usageInterface")); + response.setPrivateInterface(fwDetails.get("privateInterface")); + response.setPublicZone(fwDetails.get("publicZone")); + response.setPrivateZone(fwDetails.get("privateZone")); + response.setNumRetries(fwDetails.get("numRetries")); + response.setTimeout(fwDetails.get("timeout")); + response.setObjectName("paloaltofirewall"); + return response; + } + + @Override + public boolean verifyServicesCombination(Set services) { + if (!services.contains(Service.Firewall)) { + s_logger.warn("Palo Alto must be used as Firewall Service Provider in the network"); + return false; + } + return true; + } + + @Override + public IpDeployer getIpDeployer(Network network) { + return this; + } + + @Override + public boolean applyIps(Network network, List ipAddress, Set service) throws ResourceUnavailableException { + // return true, as IP will be associated as part of static NAT/port forwarding rule configuration + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + if (!canHandle(config, Service.StaticNat)) { + return false; + } + return applyStaticNatRules(config, rules); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoFirewallElementService.java b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoFirewallElementService.java new file mode 100644 index 00000000000..d2842ab101d --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoFirewallElementService.java @@ -0,0 +1,88 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.element; + +import java.util.List; + +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.AddPaloAltoFirewallCmd; +import com.cloud.api.commands.ConfigurePaloAltoFirewallCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.DeletePaloAltoFirewallCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.api.commands.ListPaloAltoFirewallNetworksCmd; +import com.cloud.api.commands.ListPaloAltoFirewallsCmd; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.network.dao.ExternalFirewallDeviceVO; + +import org.apache.cloudstack.api.response.ExternalFirewallResponse; +import com.cloud.utils.component.PluggableService; + +public interface PaloAltoFirewallElementService extends PluggableService { + + /** + * adds a Palo Alto firewall device in to a physical network + * @param AddPaloAltoFirewallCmd + * @return ExternalFirewallDeviceVO object for the firewall added + */ + public ExternalFirewallDeviceVO addPaloAltoFirewall(AddPaloAltoFirewallCmd cmd); + + /** + * removes Palo Alto firewall device from a physical network + * @param DeletePaloAltoFirewallCmd + * @return true if firewall device successfully deleted + */ + public boolean deletePaloAltoFirewall(DeletePaloAltoFirewallCmd cmd); + + /** + * configures a Palo Alto firewal device added in a physical network + * @param ConfigurePaloAltoFirewallCmd + * @return ExternalFirewallDeviceVO for the device configured + */ + public ExternalFirewallDeviceVO configurePaloAltoFirewall(ConfigurePaloAltoFirewallCmd cmd); + + /** + * lists all the Palo Alto firewall devices added in to a physical network + * @param ListPaloAltoFirewallsCmd + * @return list of ExternalFirewallDeviceVO for the devices in the physical network. + */ + public List listPaloAltoFirewalls(ListPaloAltoFirewallsCmd cmd); + + /** + * lists all the guest networks using a PaloAlto firewall device + * @param ListPaloAltoFirewallNetworksCmd + * @return list of the guest networks that are using this F5 load balancer + */ + public List listNetworks(ListPaloAltoFirewallNetworksCmd cmd); + + public PaloAltoFirewallResponse createPaloAltoFirewallResponse(ExternalFirewallDeviceVO fwDeviceVO); + + + @Deprecated // API helper function supported for backward compatibility + public Host addExternalFirewall(AddExternalFirewallCmd cmd); + + @Deprecated // API helper function supported for backward compatibility + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd); + + @Deprecated // API helper function supported for backward compatibility + public List listExternalFirewalls(ListExternalFirewallsCmd cmd); + + @Deprecated // API helper function supported for backward compatibility + public ExternalFirewallResponse createExternalFirewallResponse(Host externalFirewall); +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java b/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java new file mode 100644 index 00000000000..2251ce06851 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java @@ -0,0 +1,2030 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.resource; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +// http client handling +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.protocol.HTTP; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.net.URLDecoder; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import com.cloud.network.utils.HttpClientWrapper; + +// for prettyFormat() +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import java.io.StringWriter; + +public class PaloAltoResource implements ServerResource { + + private String _name; + private String _zoneId; + private String _ip; + private String _username; + private String _password; + private String _guid; + private String _key; + private Integer _numRetries; + private Integer _timeoutInSeconds; + private String _publicZone; + private String _privateZone; + private String _publicInterface; + private String _privateInterface; + private String _publicInterfaceType; + private String _privateInterfaceType; + private String _virtualRouter; + private String _threatProfile; + private String _logProfile; + private String _pingManagementProfile; + private final Logger s_logger = Logger.getLogger(PaloAltoResource.class); + + private static String _apiUri = "/api"; + private static HttpClient _httpclient; + + protected enum PaloAltoMethod { + GET, POST; + } + + private enum PaloAltoPrimative { + CHECK_IF_EXISTS, ADD, DELETE; + } + + private enum InterfaceType { + AGGREGATE("aggregate-ethernet"), + ETHERNET("ethernet"); + + private String type; + + private InterfaceType(String type) { + this.type = type; + } + public String toString() { + return type; + } + } + + private enum Protocol { + TCP("tcp"), + UDP("udp"), + ICMP("icmp"), + ALL("all"); + + private String protocol; + + private Protocol(String protocol) { + this.protocol = protocol; + } + public String toString() { + return protocol; + } + } + + private enum GuestNetworkType { + SOURCE_NAT, + INTERFACE_NAT; + } + + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand) cmd); + } else if (cmd instanceof IpAssocCommand) { + return execute((IpAssocCommand) cmd); + } else if (cmd instanceof SetStaticNatRulesCommand) { + return execute((SetStaticNatRulesCommand) cmd); + } else if (cmd instanceof SetPortForwardingRulesCommand) { + return execute((SetPortForwardingRulesCommand) cmd); + } else if (cmd instanceof SetFirewallRulesCommand) { + return execute((SetFirewallRulesCommand) cmd); + } else if (cmd instanceof ExternalNetworkResourceUsageCommand) { + return execute((ExternalNetworkResourceUsageCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + public boolean configure(String name, Map params) throws ConfigurationException { + try { + _name = (String) params.get("name"); + if (_name == null) { + throw new ConfigurationException("Unable to find name"); + } + + _zoneId = (String) params.get("zoneId"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _ip = (String) params.get("ip"); + if (_ip == null) { + throw new ConfigurationException("Unable to find IP"); + } + + _username = (String) params.get("username"); + if (_username == null) { + throw new ConfigurationException("Unable to find username"); + } + + _password = (String) params.get("password"); + if (_password == null) { + throw new ConfigurationException("Unable to find password"); + } + + _publicInterface = (String) params.get("publicinterface"); + if (_publicInterface == null) { + throw new ConfigurationException("Unable to find public interface."); + } + + _privateInterface = (String) params.get("privateinterface"); + if (_privateInterface == null) { + throw new ConfigurationException("Unable to find private interface."); + } + + _publicZone = (String) params.get("publicnetwork"); + if (_publicZone == null) { + throw new ConfigurationException("Unable to find public zone"); + } + + _privateZone = (String) params.get("privatenetwork"); + if (_privateZone == null) { + throw new ConfigurationException("Unable to find private zone"); + } + + _virtualRouter = (String) params.get("pavr"); + if (_virtualRouter == null) { + throw new ConfigurationException("Unable to find virtual router"); + } + + _threatProfile = (String) params.get("patp"); + _logProfile = (String) params.get("palp"); + + _guid = (String) params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find the guid"); + } + + _numRetries = NumbersUtil.parseInt((String) params.get("numretries"), 1); + _timeoutInSeconds = NumbersUtil.parseInt((String) params.get("timeout"), 300); + + // Open a socket and login + if (!refreshPaloAltoConnection()) { + throw new ConfigurationException("Unable to open a connection to the Palo Alto."); + } + + // check that the threat profile exists if one was specified + if (_threatProfile != null) { + try { + boolean has_profile = getThreatProfile(_threatProfile); + if (!has_profile) { + throw new ConfigurationException("The specified threat profile group does not exist."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + } + + // check that the log profile exists if one was specified + if (_logProfile != null) { + try { + boolean has_profile = getLogProfile(_logProfile); + if (!has_profile) { + throw new ConfigurationException("The specified log profile does not exist."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + } + + // get public interface type + try { + _publicInterfaceType = getInterfaceType(_publicInterface); + if (_publicInterfaceType.equals("")) { + throw new ConfigurationException("The specified public interface is not configured on the Palo Alto."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + + // get private interface type + try { + _privateInterfaceType = getInterfaceType(_privateInterface); + if (_privateInterfaceType.equals("")) { + throw new ConfigurationException("The specified private interface is not configured on the Palo Alto."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + + _pingManagementProfile = "Ping"; + try { + ArrayList cmdList = new ArrayList(); + managePingProfile(cmdList, PaloAltoPrimative.ADD); + boolean status = requestWithCommit(cmdList); + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } + + } + + public StartupCommand[] initialize() { + StartupExternalFirewallCommand cmd = new StartupExternalFirewallCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(PaloAltoResource.class.getPackage().getImplementationVersion()); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + public Host.Type getType() { + return Host.Type.ExternalFirewall; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public PingCommand getCurrentStatus(final long id) { + return new PingCommand(Host.Type.ExternalFirewall, id); + } + + @Override + public void disconnected() { + // nothing for now... + } + + public IAgentControl getAgentControl() { + return null; + } + + public void setAgentControl(IAgentControl agentControl) { + return; + } + + /* + * Login + */ + private void openHttpConnection(){ + _httpclient = new DefaultHttpClient(); + + // Allows you to connect via SSL using unverified certs + _httpclient = HttpClientWrapper.wrapClient(_httpclient); + } + + private boolean refreshPaloAltoConnection() { + if (_httpclient == null) { + openHttpConnection(); + } + + try { + return login(_username, _password); + } catch (ExecutionException e) { + s_logger.error("Failed to login due to " + e.getMessage()); + return false; + } + } + + private boolean login(String username, String password) throws ExecutionException { + Map params = new HashMap(); + params.put("type", "keygen"); + params.put("user", username); + params.put("password", password); + + String keygenBody; + try { + keygenBody = request(PaloAltoMethod.GET, params); + } catch (ExecutionException e) { + return false; + } + Document keygen_doc = getDocument(keygenBody); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result/key/text()"); + _key = (String) expr.evaluate(keygen_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (_key != null) { + return true; + } + return false; + } + + + // ENTRY POINTS... + + + private Answer execute(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + private ExternalNetworkResourceUsageAnswer execute(ExternalNetworkResourceUsageCommand cmd) { + return new ExternalNetworkResourceUsageAnswer(cmd); + } + + + /* + * Guest networks + */ + + private synchronized Answer execute(IpAssocCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(IpAssocCommand cmd, int numRetries) { + String[] results = new String[cmd.getIpAddresses().length]; + int i = 0; + try { + IpAddressTO ip; + if (cmd.getIpAddresses().length != 1) { + throw new ExecutionException("Received an invalid number of guest IPs to associate."); + } else { + ip = cmd.getIpAddresses()[0]; + } + + String sourceNatIpAddress = null; + GuestNetworkType type = GuestNetworkType.INTERFACE_NAT; + + if (ip.isSourceNat()) { + type = GuestNetworkType.SOURCE_NAT; + + if (ip.getPublicIp() == null) { + throw new ExecutionException("Source NAT IP address must not be null."); + } else { + sourceNatIpAddress = ip.getPublicIp(); + } + } + + long guestVlanTag = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG)); + String guestVlanGateway = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY); + String cidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR); + long cidrSize = NetUtils.cidrToLong(cidr)[1]; + String guestVlanSubnet = NetUtils.getCidrSubNet(guestVlanGateway, cidrSize); + + Long publicVlanTag = null; + if (ip.getBroadcastUri() != null && !ip.getBroadcastUri().equals("untagged")) { + try { + publicVlanTag = Long.parseLong(ip.getBroadcastUri()); + } catch (Exception e) { + throw new ExecutionException("Could not parse public VLAN tag: " + ip.getBroadcastUri()); + } + } + + ArrayList commandList = new ArrayList(); + + if (ip.isAdd()) { + // Implement the guest network for this VLAN + implementGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); + } else { + // Remove the guest network: + shutdownGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); + } + + boolean status = requestWithCommit(commandList); + + results[i++] = ip.getPublicIp() + " - success"; + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying IPAssocCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + results[i++] = IpAssocAnswer.errorResult; + } + } + + return new IpAssocAnswer(cmd, results); + } + + private void implementGuestNetwork(ArrayList cmdList, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrNumber) throws ExecutionException { + privateSubnet = privateSubnet+"/"+privateCidrNumber; + + managePrivateInterface(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateGateway+"/"+privateCidrNumber); + + if (type.equals(GuestNetworkType.SOURCE_NAT)) { + managePublicInterface(cmdList, PaloAltoPrimative.ADD, publicVlanTag, publicIp+"/32", privateVlanTag); + manageSrcNatRule(cmdList, PaloAltoPrimative.ADD, type, publicVlanTag, publicIp+"/32", privateVlanTag, privateGateway+"/"+privateCidrNumber); + manageNetworkIsolation(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateSubnet, privateGateway); + } + + String msg = "Implemented guest network with type " + type + ". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway+"/"+privateCidrNumber; + msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + publicIp : ""; + s_logger.debug(msg); + } + + private void shutdownGuestNetwork(ArrayList cmdList, GuestNetworkType type, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException { + privateSubnet = privateSubnet+"/"+privateCidrSize; + + if (type.equals(GuestNetworkType.SOURCE_NAT)) { + manageNetworkIsolation(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateSubnet, privateGateway); + manageSrcNatRule(cmdList, PaloAltoPrimative.DELETE, type, publicVlanTag, sourceNatIpAddress+"/32", privateVlanTag, privateGateway+"/"+privateCidrSize); + managePublicInterface(cmdList, PaloAltoPrimative.DELETE, publicVlanTag, sourceNatIpAddress+"/32", privateVlanTag); + } + + managePrivateInterface(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateGateway+"/"+privateCidrSize); + + String msg = "Shut down guest network with type " + type +". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway+"/"+privateCidrSize; + msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + sourceNatIpAddress : ""; + s_logger.debug(msg); + } + + + + /* + * Firewall rule entry point + */ + private synchronized Answer execute(SetFirewallRulesCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetFirewallRulesCommand cmd, int numRetries) { + FirewallRuleTO[] rules = cmd.getRules(); + try { + ArrayList commandList = new ArrayList(); + + for (FirewallRuleTO rule : rules) { + if (!rule.revoked()) { + manageFirewallRule(commandList, PaloAltoPrimative.ADD, rule); + } else { + manageFirewallRule(commandList, PaloAltoPrimative.DELETE, rule); + } + } + + boolean status = requestWithCommit(commandList); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetFirewallRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + + /* + * Static NAT rule entry point + */ + + private synchronized Answer execute(SetStaticNatRulesCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetStaticNatRulesCommand cmd, int numRetries) { + StaticNatRuleTO[] rules = cmd.getRules(); + + try { + ArrayList commandList = new ArrayList(); + + for (StaticNatRuleTO rule : rules) { + if (!rule.revoked()) { + manageStcNatRule(commandList, PaloAltoPrimative.ADD, rule); + } else { + manageStcNatRule(commandList, PaloAltoPrimative.DELETE, rule); + } + } + + boolean status = requestWithCommit(commandList); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetStaticNatRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + + /* + * Destination NAT (Port Forwarding) entry point + */ + private synchronized Answer execute (SetPortForwardingRulesCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetPortForwardingRulesCommand cmd, int numRetries) { + PortForwardingRuleTO[] rules = cmd.getRules(); + + try { + ArrayList commandList = new ArrayList(); + + for (PortForwardingRuleTO rule : rules) { + if (!rule.revoked()) { + manageDstNatRule(commandList, PaloAltoPrimative.ADD, rule); + } else { + manageDstNatRule(commandList, PaloAltoPrimative.DELETE, rule); + } + } + + boolean status = requestWithCommit(commandList); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetPortForwardingRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + + // IMPLEMENTATIONS... + + + /* + * Private interface implementation + */ + + private String genPrivateInterfaceName(long vlanTag) { + return _privateInterface+"."+Long.toString(vlanTag); + } + + public boolean managePrivateInterface(ArrayList cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateGateway) throws ExecutionException { + String interfaceName = genPrivateInterfaceName(privateVlanTag); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Private sub-interface exists: "+interfaceName+", "+result); + return result; + + case ADD: + if (managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) { + return true; + } + + // add cmds + // add sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']"); + a_sub_params.put("element", ""+privateVlanTag+""+_pingManagementProfile+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add sub-interface to VR... + Map a_vr_params = new HashMap(); + a_vr_params.put("type", "config"); + a_vr_params.put("action", "set"); + a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface"); + a_vr_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params)); + + // add sub-interface to vsys... + Map a_vsys_params = new HashMap(); + a_vsys_params.put("type", "config"); + a_vsys_params.put("action", "set"); + a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface"); + a_vsys_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params)); + + // add sub-interface to zone... + Map a_zone_params = new HashMap(); + a_zone_params.put("type", "config"); + a_zone_params.put("action", "set"); + a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_privateZone+"']/network/layer3"); + a_zone_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params)); + + return true; + + case DELETE: + if (!managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) { + return true; + } + + // add cmds to the list + // delete sub-interface from zone... + Map d_zone_params = new HashMap(); + d_zone_params.put("type", "config"); + d_zone_params.put("action", "delete"); + d_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_privateZone+"']/network/layer3/member[text()='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_zone_params)); + + // delete sub-interface from vsys... + Map d_vsys_params = new HashMap(); + d_vsys_params.put("type", "config"); + d_vsys_params.put("action", "delete"); + d_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface/member[text()='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vsys_params)); + + // delete sub-interface from VR... + Map d_vr_params = new HashMap(); + d_vr_params.put("type", "config"); + d_vr_params.put("action", "delete"); + d_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface/member[text()='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vr_params)); + + // delete sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Public Interface implementation + */ + + private String genPublicInterfaceName(Long id) { + return _publicInterface+"."+Long.toString(id); + } + + public boolean managePublicInterface(ArrayList cmdList, PaloAltoPrimative prim, Long publicVlanTag, String publicIp, long privateVlanTag) throws ExecutionException { + String interfaceName; + if (publicVlanTag == null) { + interfaceName = genPublicInterfaceName(new Long("9999")); + } else { + interfaceName = genPublicInterfaceName(publicVlanTag); + } + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip/entry[@name='"+publicIp+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Public sub-interface & IP exists: "+interfaceName+" : "+publicIp+", "+result); + return result; + + case ADD: + if (managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) { + return true; + } + + // add IP to the sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip"); + a_sub_params.put("element", ""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add sub-interface to VR (does nothing if already done)... + Map a_vr_params = new HashMap(); + a_vr_params.put("type", "config"); + a_vr_params.put("action", "set"); + a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface"); + a_vr_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params)); + + // add sub-interface to vsys (does nothing if already done)... + Map a_vsys_params = new HashMap(); + a_vsys_params.put("type", "config"); + a_vsys_params.put("action", "set"); + a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface"); + a_vsys_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params)); + + // add sub-interface to zone (does nothing if already done)... + Map a_zone_params = new HashMap(); + a_zone_params.put("type", "config"); + a_zone_params.put("action", "set"); + a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_publicZone+"']/network/layer3"); + a_zone_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params)); + + return true; + + case DELETE: + if (!managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) { + return true; + } + + // delete IP from sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip/entry[@name='"+publicIp+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Source NAT rule implementation + */ + + private String genSrcNatRuleName(Long privateVlanTag) { + return "src_nat."+Long.toString(privateVlanTag); + } + + public boolean manageSrcNatRule(ArrayList cmdList, PaloAltoPrimative prim, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway) throws ExecutionException { + String publicInterfaceName; + if (publicVlanTag == null) { + publicInterfaceName = genPublicInterfaceName(new Long("9999")); + } else { + publicInterfaceName = genPublicInterfaceName(publicVlanTag); + } + String srcNatName = genSrcNatRuleName(privateVlanTag); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Source NAT exists: "+srcNatName+", "+result); + return result; + + case ADD: + if (manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) { + return true; + } + + String xml = ""; + xml += ""+_privateZone+""; + xml += ""+_publicZone+""; + xml += ""+privateGateway+""; + xml += "any"; + xml += "any"; + xml += "ipv4"; + xml += ""+publicInterfaceName+""; + xml += ""; + xml += ""+publicIp+""; + xml += ""+publicInterfaceName+""; + xml += ""; + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) { + return true; + } + + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Destination NAT rules (Port Forwarding) implementation + */ + private String genDstNatRuleName(String publicIp, long id) { + return "dst_nat."+genIpIdentifier(publicIp)+"_"+Long.toString(id); + } + + public boolean manageDstNatRule(ArrayList cmdList, PaloAltoPrimative prim, PortForwardingRuleTO rule) throws ExecutionException { + String publicIp = rule.getSrcIp(); + String dstNatName = genDstNatRuleName(publicIp, rule.getId()); + + String publicInterfaceName; + String publicVlanTag = rule.getSrcVlanTag(); + if (publicVlanTag == null || publicVlanTag.equals("untagged")) { + publicInterfaceName = genPublicInterfaceName(new Long("9999")); + } else { + publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag)); + } + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Destination NAT exists: "+dstNatName+", "+result); + return result; + + case ADD: + if (manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // build source service xml + String srcService; + String protocol = rule.getProtocol(); + int[] srcPortRange = rule.getSrcPortRange(); + if (srcPortRange != null) { + String portRange; + if (srcPortRange.length == 1 || srcPortRange[0] == srcPortRange[1]) { + portRange = String.valueOf(srcPortRange[0]); + } else { + portRange = String.valueOf(srcPortRange[0])+"-"+String.valueOf(srcPortRange[1]); + } + manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null); + srcService = genServiceName(protocol, portRange, null); + } else { + // no equivalent config in PA, so allow all traffic... + srcService = "any"; + } + + // build destination port xml (single port limit in PA) + String dstPortXML = ""; + int[] dstPortRange = rule.getDstPortRange(); + if (dstPortRange != null) { + dstPortXML = ""+dstPortRange[0]+""; + } + + // add public IP to the sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip"); + a_sub_params.put("element", ""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add the destination nat rule for the public IP + String xml = ""; + xml += ""+_publicZone+""; + xml += ""+_publicZone+""; + xml += "any"; + xml += ""+publicIp+""; + xml += ""+srcService+""; + xml += "ipv4"; + xml += ""+publicInterfaceName+""; + xml += ""+rule.getDstIp()+""+dstPortXML+""; + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // determine if we need to delete the ip from the interface as well... + Map c_params = new HashMap(); + c_params.put("type", "config"); + c_params.put("action", "get"); + c_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[destination/member[text()='"+publicIp+"']]"); + String c_response = request(PaloAltoMethod.GET, c_params); + + String count = ""; + NodeList response_body; + Document doc = getDocument(c_response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (response_body.getLength() > 0 && response_body.item(0).getAttributes().getLength() > 0) { + count = response_body.item(0).getAttributes().getNamedItem("count").getTextContent(); + } + + // delete the dst nat rule + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + if (!count.equals("") && Integer.parseInt(count) == 1) { // this dst nat rule is the last, so remove the ip... + // delete IP from sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip/entry[@name='"+publicIp+"/32']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + } + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + + /* + * Static NAT rule implementation + */ + private String genStcNatRuleName(String publicIp, long id) { + return "stc_nat."+genIpIdentifier(publicIp)+"_"+Long.toString(id); + } + + public boolean manageStcNatRule(ArrayList cmdList, PaloAltoPrimative prim, StaticNatRuleTO rule) throws ExecutionException { + String publicIp = rule.getSrcIp(); + String stcNatName = genStcNatRuleName(publicIp, rule.getId()); + + String publicInterfaceName; + String publicVlanTag = rule.getSrcVlanTag(); + if (publicVlanTag == null || publicVlanTag.equals("untagged")) { + publicInterfaceName = genPublicInterfaceName(new Long("9999")); + } else { + publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag)); + } + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Static NAT exists: "+stcNatName+", "+result); + return result; + + case ADD: + if (manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // add public IP to the sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip"); + a_sub_params.put("element", ""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add the static nat rule for the public IP + String xml = ""; + xml += ""+_publicZone+""; + xml += ""+_publicZone+""; + xml += "any"; + xml += ""+publicIp+""; + xml += "any"; + xml += "ipv4"; + xml += ""+publicInterfaceName+""; + xml += ""+rule.getDstIp()+""; + + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // delete the static nat rule + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + // delete IP from sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip/entry[@name='"+publicIp+"/32']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Firewall rule implementation + */ + + private String genFirewallRuleName(long id) { + return "policy_"+Long.toString(id); + } + + public boolean manageFirewallRule(ArrayList cmdList, PaloAltoPrimative prim, FirewallRuleTO rule) throws ExecutionException { + String ruleName = genFirewallRuleName(rule.getId()); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Firewall policy exists: "+ruleName+", "+result); + return result; + + case ADD: + if (manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + String srcZone; + String dstZone; + String dstAddressXML; + String appXML; + String serviceXML; + + String protocol = rule.getProtocol(); + + // Only ICMP will use an Application, so others will be any. + if (protocol.equals(Protocol.ICMP.toString())) { + appXML = "icmppingtraceroute"; // use the default icmp applications... + } else { + appXML = "any"; + } + + // Only TCP and UDP will use a Service, others will use any. + if (protocol.equals(Protocol.TCP.toString()) || protocol.equals(Protocol.UDP.toString())) { + String portRange; + if (rule.getSrcPortRange() != null) { + int startPort = rule.getSrcPortRange()[0]; + int endPort = rule.getSrcPortRange()[1]; + if (startPort == endPort) { + portRange = String.valueOf(startPort); + } else { + portRange = String.valueOf(startPort)+"-"+String.valueOf(endPort); + } + manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null); + serviceXML = ""+genServiceName(protocol, portRange, null)+""; + } else { + // no equivalent config in PA, so allow all traffic... + serviceXML = "any"; + } + } else { + serviceXML = "any"; + } + + if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { // Network egress rule + srcZone = _privateZone; + dstZone = _publicZone; + dstAddressXML = "any"; + } else { + srcZone = _publicZone; + dstZone = _privateZone; + dstAddressXML = ""+rule.getSrcIp()+""; + } + + // build the source cidr xml + String srcCidrXML = ""; + List ruleSrcCidrList = rule.getSourceCidrList(); + if (ruleSrcCidrList.size() > 0) { // a cidr was entered, modify as needed... + for (int i = 0; i < ruleSrcCidrList.size(); i++) { + if (ruleSrcCidrList.get(i).trim().equals("0.0.0.0/0")) { // allow any + if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { + srcCidrXML += ""+getPrivateSubnet(rule.getSrcVlanTag())+""; + } else { + srcCidrXML += "any"; + } + } else { + srcCidrXML += ""+ruleSrcCidrList.get(i).trim()+""; + } + } + } else { // no cidr was entered, so allow ALL according to firewall rule type + if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { + srcCidrXML = ""+getPrivateSubnet(rule.getSrcVlanTag())+""; + } else { + srcCidrXML = "any"; + } + } + + String xml = ""; + xml += ""+srcZone+""; + xml += ""+dstZone+""; + xml += ""+srcCidrXML+""; + xml += ""+dstAddressXML+""; + xml += ""+appXML+""; + xml += ""+serviceXML+""; + xml += "allow"; + xml += "no"; + xml += "no"; + if (_threatProfile != null) { // add the threat profile if it exists + xml += ""+_threatProfile+""; + } + if (_logProfile != null) { // add the log profile if it exists + xml += ""+_logProfile+""; + } + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + + /* + * Usage + */ + + + + /* + * Helper config functions + */ + + // ensure guest network isolation + private String genNetworkIsolationName(long privateVlanTag) { + return "isolate_"+Long.toString(privateVlanTag); + } + + public boolean manageNetworkIsolation(ArrayList cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateSubnet, String privateGateway) throws ExecutionException { + String ruleName = genNetworkIsolationName(privateVlanTag); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Firewall policy exists: "+ruleName+", "+result); + return result; + + case ADD: + if (manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) { + return true; + } + + String xml = ""; + xml += ""+_privateZone+""; + xml += ""+_privateZone+""; + xml += ""+privateSubnet+""; + xml += ""+privateGateway+""; + xml += "any"; + xml += "any"; + xml += "deny"; + xml += "no"; + xml += "yes"; + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) { + return true; + } + + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + // make the interfaces pingable for basic network troubleshooting + public boolean managePingProfile(ArrayList cmdList, PaloAltoPrimative prim) throws ExecutionException { + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Management profile exists: "+_pingManagementProfile+", "+result); + return result; + + case ADD: + if (managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) { + return true; + } + + // add ping profile... + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']"); + a_params.put("element", "yes"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params)); + + return true; + + case DELETE: + if (!managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) { + return true; + } + + // delete ping profile... + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + private String genServiceName(String protocol, String dstPorts, String srcPorts) { + String name; + if (srcPorts == null) { + name = "cs_"+protocol.toLowerCase()+"_"+dstPorts.replace(',', '.'); + } else { + name = "cs_"+protocol.toLowerCase()+"_"+dstPorts.replace(',', '.')+"_"+srcPorts.replace(',', '.'); + } + return name; + } + + public boolean manageService(ArrayList cmdList, PaloAltoPrimative prim, String protocol, String dstPorts, String srcPorts) throws ExecutionException { + String serviceName = genServiceName(protocol, dstPorts, srcPorts); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Service exists: "+serviceName+", "+result); + return result; + + case ADD: + if (manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) { + return true; + } + + String dstPortXML = ""+dstPorts+""; + String srcPortXML = ""; + if (srcPorts != null) { + srcPortXML = ""+srcPorts+""; + } + + // add ping profile... + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']"); + a_params.put("element", "<"+protocol.toLowerCase()+">"+dstPortXML+srcPortXML+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params)); + + return true; + + case DELETE: + if (!manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) { + return true; + } + + // delete ping profile... + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + private String getPrivateSubnet(String vlan) throws ExecutionException { + String _interfaceName = genPrivateInterfaceName(Long.valueOf(vlan).longValue()); + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+_interfaceName+"']/ip/entry"); + String response = request(PaloAltoMethod.GET, params); + if (validResponse(response) && responseNotEmpty(response)) { + NodeList response_body; + Document doc = getDocument(response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result/entry"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (response_body.getLength() > 0) { + return response_body.item(0).getAttributes().getNamedItem("name").getTextContent(); + } + } + return null; + } + + + /* + * XML API commands + */ + + /* Function to make calls to the Palo Alto API. */ + /* All API calls will end up going through this function. */ + protected String request(PaloAltoMethod method, Map params) throws ExecutionException { + if (method != PaloAltoMethod.GET && method != PaloAltoMethod.POST) { + throw new ExecutionException("Invalid http method used to access the Palo Alto API."); + } + + String responseBody = ""; + String debug_msg = "Palo Alto Request\n"; + + // a GET method... + if (method == PaloAltoMethod.GET) { + String queryString = "?"; + for (String key : params.keySet()) { + if (!queryString.equals("?")) { + queryString = queryString + "&"; + } + try { + queryString = queryString + key+"="+URLEncoder.encode(params.get(key), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new ExecutionException(e.getMessage()); + } + } + if (_key != null) { + queryString = queryString + "&key="+_key; + } + + try { + debug_msg = debug_msg + "GET request: https://" + _ip + _apiUri + URLDecoder.decode(queryString, "UTF-8") + "\n"; + } catch (UnsupportedEncodingException e) { + debug_msg = debug_msg + "GET request: https://" + _ip + _apiUri + queryString + "\n"; + } + + + HttpGet get_request = new HttpGet("https://" + _ip + _apiUri + queryString); + ResponseHandler responseHandler = new BasicResponseHandler(); + try { + responseBody = _httpclient.execute(get_request, responseHandler); + } catch (IOException e) { + throw new ExecutionException(e.getMessage()); + } + } + + // a POST method... + if (method == PaloAltoMethod.POST) { + List nvps = new ArrayList (); + for (String key : params.keySet()) { + nvps.add(new BasicNameValuePair(key, params.get(key))); + } + if (_key != null) { + nvps.add(new BasicNameValuePair("key", _key)); + } + + debug_msg = debug_msg + "POST request: https://" + _ip + _apiUri + "\n"; + for (NameValuePair nvp : nvps) { + debug_msg = debug_msg + "param: "+nvp.getName()+", "+nvp.getValue() + "\n"; + } + + HttpPost post_request = new HttpPost("https://" + _ip + _apiUri); + try { + post_request.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); + } catch (UnsupportedEncodingException e) { + throw new ExecutionException(e.getMessage()); + } + ResponseHandler responseHandler = new BasicResponseHandler(); + try { + responseBody = _httpclient.execute(post_request, responseHandler); + } catch (IOException e) { + throw new ExecutionException(e.getMessage()); + } + } + + debug_msg = debug_msg + prettyFormat(responseBody); + debug_msg = debug_msg + "\n" + responseBody.replace("\"", "\\\"") + "\n\n"; // test cases + //s_logger.debug(debug_msg); // this can be commented if we don't want to show each request in the log. + + return responseBody; + } + + /* Used for requests that require polling to get a result (eg: commit) */ + private String requestWithPolling(PaloAltoMethod method, Map params) throws ExecutionException { + String job_id; + String job_response = request(method, params); + Document doc = getDocument(job_response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result/job/text()"); + job_id = (String) expr.evaluate(doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (job_id.length() > 0) { + boolean finished = false; + Map job_params = new HashMap(); + job_params.put("type", "op"); + job_params.put("cmd", ""+job_id+""); + + while (!finished) { + String job_status; + String response = request(PaloAltoMethod.GET, job_params); + Document job_doc = getDocument(response); + XPath job_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/status/text()"); + job_status = (String) expr.evaluate(job_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (job_status.equals("FIN")) { + finished = true; + String job_result; + try { + XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/result/text()"); + job_result = (String) expr.evaluate(job_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (!job_result.equals("OK")) { + NodeList job_details; + try { + XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/details/line"); + job_details = (NodeList) expr.evaluate(job_doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + String error = ""; + for (int i = 0; i < job_details.getLength(); i++) { + error = error + job_details.item(i).getTextContent() + "\n"; + } + throw new ExecutionException(error); + } + return response; + } else { + try { + Thread.sleep(2000); // poll periodically for the status of the async job... + } catch (InterruptedException e) { /* do nothing */ } + } + } + } else { + return job_response; + } + return null; + } + + /* Runs a sequence of commands and attempts to commit at the end. */ + /* Uses the Command pattern to enable overriding of the response handling if needed. */ + private synchronized boolean requestWithCommit(ArrayList commandList) throws ExecutionException { + boolean result = true; + + if (commandList.size() > 0) { + // CHECK IF THERE IS PENDING CHANGES THAT HAVE NOT BEEN COMMITTED... + String pending_changes; + Map check_params = new HashMap(); + check_params.put("type", "op"); + check_params.put("cmd", ""); + String check_response = request(PaloAltoMethod.GET, check_params); + Document check_doc = getDocument(check_response); + XPath check_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = check_xpath.compile("/response[@status='success']/result/text()"); + pending_changes = (String) expr.evaluate(check_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (pending_changes.equals("yes")) { + throw new ExecutionException("The Palo Alto has uncommited changes, so no changes can be made. Try again later or contact your administrator."); + } else { + // ADD A CONFIG LOCK TO CAPTURE THE PALO ALTO RESOURCE + String add_lock_status; + Map add_lock_params = new HashMap(); + add_lock_params.put("type", "op"); + add_lock_params.put("cmd", ""); + String add_lock_response = request(PaloAltoMethod.GET, add_lock_params); + Document add_lock_doc = getDocument(add_lock_response); + XPath add_lock_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = add_lock_xpath.compile("/response[@status='success']/result/text()"); + add_lock_status = (String) expr.evaluate(add_lock_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (add_lock_status.length() == 0) { + throw new ExecutionException("The Palo Alto is locked, no changes can be made at this time."); + } + + try { + // RUN THE SEQUENCE OF COMMANDS + for (IPaloAltoCommand command : commandList) { + result = (result && command.execute()); // run commands and modify result boolean + } + + // COMMIT THE CHANGES (ALSO REMOVES CONFIG LOCK) + String commit_job_id; + Map commit_params = new HashMap(); + commit_params.put("type", "commit"); + commit_params.put("cmd", ""); + String commit_response = requestWithPolling(PaloAltoMethod.GET, commit_params); + Document commit_doc = getDocument(commit_response); + XPath commit_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = commit_xpath.compile("/response[@status='success']/result/job/id/text()"); + commit_job_id = (String) expr.evaluate(commit_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (commit_job_id.length() == 0) { // no commit was done, so release the lock... + // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE + String remove_lock_status; + Map remove_lock_params = new HashMap(); + remove_lock_params.put("type", "op"); + remove_lock_params.put("cmd", ""); + String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params); + Document remove_lock_doc = getDocument(remove_lock_response); + XPath remove_lock_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()"); + remove_lock_status = (String) expr.evaluate(remove_lock_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (remove_lock_status.length() == 0) { + throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!"); + } + } + + } catch (ExecutionException ex) { + // REVERT TO RUNNING + String revert_job_id; + Map revert_params = new HashMap(); + revert_params.put("type", "op"); + revert_params.put("cmd", "running-config.xml"); + requestWithPolling(PaloAltoMethod.GET, revert_params); + + // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE + String remove_lock_status; + Map remove_lock_params = new HashMap(); + remove_lock_params.put("type", "op"); + remove_lock_params.put("cmd", ""); + String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params); + Document remove_lock_doc = getDocument(remove_lock_response); + XPath remove_lock_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()"); + remove_lock_status = (String) expr.evaluate(remove_lock_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (remove_lock_status.length() == 0) { + throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!"); + } + + throw ex; // Bubble up the reason we reverted... + } + + return result; + } + } else { + return true; // nothing to do + } + } + + /* A default response handler to validate that the request was successful. */ + public boolean validResponse(String response) throws ExecutionException { + NodeList response_body; + Document doc = getDocument(response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + + if (response_body.getLength() > 0) { + return true; + } else { + NodeList error_details; + try { + XPathExpression expr = xpath.compile("/response/msg/line/line"); + error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (error_details.getLength() == 0) { + try { + XPathExpression expr = xpath.compile("/response/msg/line"); + error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + + if (error_details.getLength() == 0) { + try { + XPathExpression expr = xpath.compile("/response/result/msg"); + error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + } + } + String error = ""; + for (int i = 0; i < error_details.getLength(); i++) { + error = error + error_details.item(i).getTextContent() + "\n"; + } + throw new ExecutionException(error); + } + } + + /* Validate that the response is not empty. */ + public boolean responseNotEmpty(String response) throws ExecutionException { + NodeList response_body; + Document doc = getDocument(response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + + if (response_body.getLength() > 0 && + (!response_body.item(0).getTextContent().equals("") || + (response_body.item(0).hasChildNodes() && response_body.item(0).getFirstChild().hasChildNodes()))) { + return true; + } else { + return false; + } + } + + /* Get the type of interface from the PA device. */ + private String getInterfaceType(String interface_name) throws ExecutionException { + String[] types = { InterfaceType.ETHERNET.toString(), InterfaceType.AGGREGATE.toString() }; + for (String type : types) { + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+type+"/entry[@name='"+interface_name+"']"); + String ethernet_response = request(PaloAltoMethod.GET, params); + if (validResponse(ethernet_response) && responseNotEmpty(ethernet_response)) { + return type; + } + } + return ""; + } + + /* Get the threat profile from the server if it exists. */ + private boolean getThreatProfile(String profile) throws ExecutionException { + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/profile-group/entry[@name='"+profile+"']"); + String response = request(PaloAltoMethod.GET, params); + return (validResponse(response) && responseNotEmpty(response)); + } + + /* Get the log profile from the server if it exists. */ + private boolean getLogProfile(String profile) throws ExecutionException { + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/log-settings/profiles/entry[@name='"+profile+"']"); + String response = request(PaloAltoMethod.GET, params); + return (validResponse(response) && responseNotEmpty(response)); + } + + /* Command Interface */ + public interface IPaloAltoCommand { + public boolean execute() throws ExecutionException; + } + + /* Command Abstract */ + private abstract class AbstractPaloAltoCommand implements IPaloAltoCommand { + PaloAltoMethod method; + Map params; + + public AbstractPaloAltoCommand() {} + + public AbstractPaloAltoCommand(PaloAltoMethod method, Map params) { + this.method = method; + this.params = params; + } + + public boolean execute() throws ExecutionException { + String response = request(method, params); + return validResponse(response); + } + } + + /* Implement the default functionality */ + private class DefaultPaloAltoCommand extends AbstractPaloAltoCommand { + public DefaultPaloAltoCommand(PaloAltoMethod method, Map params) { + super(method, params); + } + } + + + /* + * Misc + */ + + private String genIpIdentifier(String ip) { + return ip.replace('.', '-').replace('/', '-'); + } + + private Protocol getProtocol(String protocolName) throws ExecutionException { + protocolName = protocolName.toLowerCase(); + + try { + return Protocol.valueOf(protocolName); + } catch (Exception e) { + throw new ExecutionException("Invalid protocol: " + protocolName); + } + } + + private Document getDocument(String xml) throws ExecutionException { + StringReader xmlReader = new StringReader(xml); + InputSource xmlSource = new InputSource(xmlReader); + Document doc = null; + + try { + doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlSource); + } catch (Exception e) { + s_logger.error(e); + throw new ExecutionException(e.getMessage()); + } + + if (doc == null) { + throw new ExecutionException("Failed to parse xml " + xml); + } else { + return doc; + } + } + + private String prettyFormat(String input) { + int indent = 4; + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", indent); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(xmlInput, xmlOutput); + return xmlOutput.getWriter().toString(); + } catch (Throwable e) { + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent)); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(xmlInput, xmlOutput); + return xmlOutput.getWriter().toString(); + } catch(Throwable t) { + return input; + } + } + } + + //@Override + public void setName(String name) { + // TODO Auto-generated method stub + + } + + //@Override + public void setConfigParams(Map params) { + // TODO Auto-generated method stub + + } + + //@Override + public Map getConfigParams() { + // TODO Auto-generated method stub + return null; + } + + //@Override + public int getRunLevel() { + // TODO Auto-generated method stub + return 0; + } + + //@Override + public void setRunLevel(int level) { + // TODO Auto-generated method stub + + } + +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/utils/HttpClientWrapper.java b/plugins/network-elements/palo-alto/src/com/cloud/network/utils/HttpClientWrapper.java new file mode 100644 index 00000000000..7e4057005cb --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/utils/HttpClientWrapper.java @@ -0,0 +1,69 @@ +package com.cloud.network.utils; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; + +import java.io.*; + +public class HttpClientWrapper { + + public static HttpClient wrapClient(HttpClient base) { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + + public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + X509HostnameVerifier verifier = new X509HostnameVerifier() { + + @Override + public void verify(String string, SSLSocket ssls) throws IOException { + } + + @Override + public void verify(String string, X509Certificate xc) throws SSLException { + } + + @Override + public void verify(String string, String[] strings, String[] strings1) throws SSLException { + } + + @Override + public boolean verify(String string, SSLSession ssls) { + return true; + } + }; + ctx.init(null, new TrustManager[]{tm}, null); + SSLSocketFactory ssf = new SSLSocketFactory(ctx); + ssf.setHostnameVerifier(verifier); + ClientConnectionManager ccm = base.getConnectionManager(); + SchemeRegistry sr = ccm.getSchemeRegistry(); + sr.register(new Scheme("https", ssf, 443)); + return new DefaultHttpClient(ccm, base.getParams()); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/test/com/cloud/network/resource/MockablePaloAltoResource.java b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/MockablePaloAltoResource.java new file mode 100755 index 00000000000..9a9eb6e0c2e --- /dev/null +++ b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/MockablePaloAltoResource.java @@ -0,0 +1,460 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.resource; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +// http client handling +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.protocol.HTTP; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.net.URLDecoder; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import com.cloud.network.utils.HttpClientWrapper; + +// for prettyFormat() +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import java.io.StringWriter; + + +public class MockablePaloAltoResource extends PaloAltoResource { + private HashMap context; + public void setMockContext(HashMap context) { + this.context = context; + } + + /* Fake the calls to the Palo Alto API */ + protected String request(PaloAltoMethod method, Map params) throws ExecutionException { + if (method != PaloAltoMethod.GET && method != PaloAltoMethod.POST) { + throw new ExecutionException("Invalid http method used to access the Palo Alto API."); + } + + String response = ""; + + // 'keygen' request + if (params.containsKey("type") && params.get("type").equals("keygen")) { + response = "LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09"; + } + + // 'config' requests + if (params.containsKey("type") && params.get("type").equals("config") && params.containsKey("action")) { + // action = 'get' + if (params.get("action").equals("get")) { + // get interface for type + // | public_using_ethernet + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']")) { + if (context.containsKey("public_using_ethernet") && context.get("public_using_ethernet").equals("true")) { + context.put("public_interface_type", "ethernet"); + response = "no20060064unspecifiedunspecified1800nononounspecifiedno3011noEUI-64nono20060064unspecifiedunspecified1800nononounspecifiedno3011noEUI-64no3033autoautoauto"; + } else { + response = ""; + } + } // | private_using_ethernet + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']")) { + if (context.containsKey("private_using_ethernet") && context.get("private_using_ethernet").equals("true")) { + context.put("private_interface_type", "ethernet"); + response = "no20060064unspecifiedunspecified1800nononounspecifiedno3011noEUI-64noautoautoauto"; + } else { + response = ""; + } + } + + // get management profile | has_management_profile + if (params.get("xpath").equals("/config/devices/entry/network/profiles/interface-management-profile/entry[@name='Ping']")) { + if (context.containsKey("has_management_profile") && context.get("has_management_profile").equals("true")) { + response = "yes"; + } else { + response = ""; + } + } + + // get public interface IP | has_public_interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip/entry[@name='192.168.80.102/32']")) { + if (context.containsKey("has_public_interface") && context.get("has_public_interface").equals("true")) { + response = ""; + } else { + response = ""; + } + } + + // get private interface | has_private_interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']")) { + if (context.containsKey("has_private_interface") && context.get("has_private_interface").equals("true")) { + response = "3954Ping"; + } else { + response = ""; + } + } + + // get private interface ip + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']/ip/entry")) { + response = ""; + } + + // get source nat | has_src_nat_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='src_nat.3954']")) { + if (context.containsKey("has_src_nat_rule") && context.get("has_src_nat_rule").equals("true")) { + response = "untrusttrust10.5.80.1/20anyanyipv4ethernet1/1.9999192.168.80.102/32ethernet1/1.9999"; + } else { + response = ""; + } + } + + // get isolation firewall rule | has_isolation_fw_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='isolate_3954']")) { + if (context.containsKey("has_isolation_fw_rule") && context.get("has_isolation_fw_rule").equals("true")) { + response = "trusttrust10.5.80.0/2010.5.80.1anyanydenynoyes"; + } else { + response = ""; + } + } + + // get service | has_service + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='cs_tcp_80']")) { + if (context.containsKey("has_service_tcp_80") && context.get("has_service_tcp_80").equals("true")) { + response = "80"; + } else { + response = ""; + } + } + + // get egress firewall rule | has_egress_fw_rule | policy_0 + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0']")) { + if (context.containsKey("has_egress_fw_rule") && context.get("has_egress_fw_rule").equals("true")) { + response = "trustuntrust10.3.96.1/20anyanycs_tcp_80allownono"; + } else { + response = ""; + } + } + + // get ingress firewall rule | has_ingress_fw_rule | policy_8 + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_8']")) { + if (context.containsKey("has_ingress_fw_rule") && context.get("has_ingress_fw_rule").equals("true")) { + response = "untrusttrustany192.168.80.103anycs_tcp_80allownono"; + } else { + response = ""; + } + } + + // get destination nat rule (port forwarding) | has_dst_nat_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='dst_nat.192-168-80-103_9']")) { + if (context.containsKey("has_dst_nat_rule") && context.get("has_dst_nat_rule").equals("true")) { + response = "untrustuntrustany192.168.80.103cs_tcp_80ipv4ethernet1/1.999910.3.97.1588080"; + } else { + response = ""; + } + } + + // get destination nat rules (returns all dst nat rules per ip) + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[destination/member[text()='192.168.80.103']]")) { + if (context.containsKey("has_dst_nat_rule") && context.get("has_dst_nat_rule").equals("true")) { + response = "untrustuntrustany192.168.80.103cs_tcp_80ipv4ethernet1/1.999910.3.97.1588080"; + } else { + response = ""; + } + } + + // get static nat rule | has_stc_nat_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='stc_nat.192-168-80-103_0']")) { + if (context.containsKey("has_stc_nat_rule") && context.get("has_stc_nat_rule").equals("true")) { + response = "untrustuntrustany192.168.80.103anyipv4ethernet1/1.999910.3.97.158"; + } else { + response = ""; + } + } + + } + + // action = 'set' + if (params.get("action").equals("set")) { + // set management profile + if (params.get("xpath").equals("/config/devices/entry/network/profiles/interface-management-profile/entry[@name='Ping']")) { + response = "command succeeded"; + context.put("has_management_profile", "true"); + } + + // add private interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']")) { + response = "command succeeded"; + context.put("has_private_interface", "true"); + } + + // add public ip to public interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip")) { + response = "command succeeded"; + context.put("has_public_interface", "true"); + } + + // add private interface to zone + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='trust']/network/layer3")) { + response = "command succeeded"; + } + + // add public interface to zone + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='untrust']/network/layer3")) { + response = "command succeeded"; + } + + // set virtual router (public | private) + if (params.get("xpath").equals("/config/devices/entry/network/virtual-router/entry[@name='default']/interface")) { + response = "command succeeded"; + } + + // add interface to network (public | private) + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface")) { + response = "command succeeded"; + } + + // add src nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='src_nat.3954']")) { + response = "command succeeded"; + context.put("has_src_nat_rule", "true"); + } + + // add isolation firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='isolate_3954']")) { + response = "command succeeded"; + context.put("has_isolation_fw_rule", "true"); + } + + // add egress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0']")) { + response = "command succeeded"; + context.put("has_egress_fw_rule", "true"); + } + + // add ingress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_8']")) { + response = "command succeeded"; + context.put("has_ingress_fw_rule", "true"); + } + + // add destination nat rule (port forwarding) + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='dst_nat.192-168-80-103_9']")) { + response = "command succeeded"; + context.put("has_dst_nat_rule", "true"); + } + + // add static nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='stc_nat.192-168-80-103_0']")) { + response = "command succeeded"; + context.put("has_stc_nat_rule", "true"); + } + + // add tcp 80 service + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='cs_tcp_80']")) { + response = "command succeeded"; + context.put("has_service_tcp_80", "true"); + } + } + + // action = 'delete' + if (params.get("action").equals("delete")) { + // remove egress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0']")) { + response = "command succeeded"; + context.remove("has_egress_fw_rule"); + } + + // remove ingress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_8']")) { + response = "command succeeded"; + context.remove("has_ingress_fw_rule"); + } + + // remove destination nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='dst_nat.192-168-80-103_9']")) { + response = "command succeeded"; + context.remove("has_dst_nat_rule"); + } + + // remove static nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='stc_nat.192-168-80-103_0']")) { + response = "command succeeded"; + context.remove("has_dst_nat_rule"); + } + + // remove public ip from interface (dst_nat | stc_nat) + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip/entry[@name='192.168.80.103/32']")) { + response = "command succeeded"; + } + + // remove isolation firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='isolate_3954']")) { + response = "command succeeded"; + context.remove("has_isolation_fw_rule"); + } + + // remove source nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='src_nat.3954']")) { + response = "command succeeded"; + context.remove("has_src_nat_rule"); + } + + // remove public source nat ip + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip/entry[@name='192.168.80.102/32']")) { + response = "command succeeded"; + context.remove("has_public_interface"); + } + + // remove private interface from the zone + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='trust']/network/layer3/member[text()='ethernet1/2.3954']")) { + response = "command succeeded"; + } + + // remove private interface from the virtual router + if (params.get("xpath").equals("/config/devices/entry/network/virtual-router/entry[@name='default']/interface/member[text()='ethernet1/2.3954']")) { + response = "command succeeded"; + } + + // remove private interface from network + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface/member[text()='ethernet1/2.3954']")) { + response = "command succeeded"; + } + + // remove private interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']")) { + response = "command succeeded"; + context.remove("has_private_interface"); + } + + } + } // end 'config' + + // 'op' requests + if (params.containsKey("type") && params.get("type").equals("op")) { + // check if there are pending changes + if (params.get("cmd").equals("")) { + if (context.containsKey("firewall_has_pending_changes") && context.get("firewall_has_pending_changes").equals("true")) { + response = "yes"; + } else { + response = "no"; + } + } + + // add a config lock + if (params.get("cmd").equals("")) { + response = "Successfully acquired lock. Other administrators will not be able to modify configuration for scope shared until lock is released"; + } + + // check job status + if (params.get("cmd").equals("1")) { + if (context.containsKey("simulate_commit_failure") && context.get("simulate_commit_failure").equals("true")) { + response = "2013/07/10 11:11:491adminCommitFINnoFAIL11:11:5411:11:54
Bad configCommit failed
"; + } else { + response = "2013/07/02 14:49:491adminCommitFINnoOK14:50:0214:50:02
Configuration committed successfully
"; + } + } + + // load from running config + if (params.get("cmd").equals("running-config.xml")) { + response = "Config loaded from running-config.xml"; + } + + // remove config lock + if (params.get("cmd").equals("")) { + response = "Config lock released for scope shared"; + } + } // end 'op' + + // 'commit' requests + if (params.containsKey("type") && params.get("type").equals("commit")) { + // cmd = '' + if (params.get("cmd").equals("")) { + response = "Commit job enqueued with jobid 11"; + } + } // end 'commit' + + + // print out the details into the console + if (context.containsKey("enable_console_output") && context.get("enable_console_output") == "true") { + if (params.containsKey("xpath")) { + System.out.println("XPATH("+params.get("action")+"): "+params.get("xpath")); + } + if (params.containsKey("type") && params.get("type").equals("op")) { + System.out.println("OP CMD: "+params.get("cmd")); + } + System.out.println(response+"\n"); + } + + return response; + } +} \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/test/com/cloud/network/resource/PaloAltoResourceTest.java b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/PaloAltoResourceTest.java new file mode 100755 index 00000000000..c2704734c6c --- /dev/null +++ b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/PaloAltoResourceTest.java @@ -0,0 +1,507 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.resource; + +// test imports +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +// basic imports +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.FirewallRule.State; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +// http client handling +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.protocol.HTTP; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.net.URLDecoder; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import com.cloud.network.utils.HttpClientWrapper; + +// for prettyFormat() +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import java.io.StringWriter; + +public class PaloAltoResourceTest { + // configuration data + private String _test_name = "PaloAltoTestDevice"; + private String _test_zoneId = "TestZone"; + private String _test_ip = "192.168.80.2"; + private String _test_username = "admin"; + private String _test_password = "admin"; + private String _test_publicInterface = "ethernet1/1"; + private String _test_privateInterface = "ethernet1/2"; + private String _test_publicZone = "untrust"; + private String _test_privateZone = "trust"; + private String _test_virtualRouter = "default"; + + MockablePaloAltoResource _resource; + Map _resource_params; + HashMap _context; + + @Before + public void setUp() { + _resource = new MockablePaloAltoResource(); + _resource_params = new HashMap(); // params to be passed to configure() + _resource_params.put("name", _test_name); + _resource_params.put("zoneId", _test_zoneId); + _resource_params.put("ip", _test_ip); + _resource_params.put("username", _test_username); + _resource_params.put("password", _test_password); + _resource_params.put("publicinterface", _test_publicInterface); + _resource_params.put("privateinterface", _test_privateInterface); + _resource_params.put("publicnetwork", _test_publicZone); + _resource_params.put("privatenetwork", _test_privateZone); + _resource_params.put("pavr", _test_virtualRouter); + _resource_params.put("guid", "aaaaa-bbbbb-ccccc"); + + _context = new HashMap(); // global context + _context.put("name", _test_name); + _context.put("zone_id", _test_zoneId); + _context.put("ip", _test_ip); + _context.put("username", _test_username); + _context.put("password", _test_password); + _context.put("public_interface", _test_publicInterface); + _context.put("private_interface", _test_privateInterface); + _context.put("public_zone", _test_publicZone); + _context.put("private_zone", _test_privateZone); + _context.put("pa_vr", _test_virtualRouter); + // -- + _context.put("public_using_ethernet", "true"); + _context.put("private_using_ethernet", "true"); + _context.put("has_management_profile", "true"); + _context.put("enable_console_output", "false"); // CHANGE TO "true" TO ENABLE CONSOLE LOGGING OF TESTS + _resource.setMockContext(_context); + } + + @Test (expected=ConfigurationException.class) + public void resourceConfigureFailure() throws ConfigurationException { + _resource.configure("PaloAltoResource", new HashMap()); + } + + @Test + public void resourceConfigureWithoutManagementProfile() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: resourceConfigureWithoutManagementProfile"); + System.out.println("---------------------------------------------------"); + } + _context.remove("has_management_profile"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test + public void resourceConfigureWithManagementProfile() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: resourceConfigureWithManagementProfile"); + System.out.println("---------------------------------------------------"); + } + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test (expected=ConfigurationException.class) + public void simulateFirewallNotConfigurable() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: simulateFirewallNotConfigurable"); + System.out.println("---------------------------------------------------"); + } + _context.put("firewall_has_pending_changes", "true"); + _context.remove("has_management_profile"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test (expected=ConfigurationException.class) + public void simulateFirewallCommitFailure() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: simulateFirewallCommitFailure"); + System.out.println("---------------------------------------------------"); + } + _context.put("simulate_commit_failure", "true"); + _context.remove("has_management_profile"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test + public void testInitialize() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: testInitialization"); + System.out.println("---------------------------------------------------"); + } + _resource.configure("PaloAltoResource", _resource_params); + + StartupCommand[] sc = _resource.initialize(); + assertTrue(sc.length == 1); + assertTrue("aaaaa-bbbbb-ccccc".equals(sc[0].getGuid())); + assertTrue("PaloAltoTestDevice".equals(sc[0].getName())); + assertTrue("TestZone".equals(sc[0].getDataCenter())); + } + + @Test // implement public & private interfaces, source nat, guest network + public void implementGuestNetwork() throws ConfigurationException, ExecutionException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: implementGuestNetwork"); + System.out.println("---------------------------------------------------"); + } + _resource.configure("PaloAltoResource", _resource_params); + + IpAddressTO ip = new IpAddressTO(Long.valueOf("1"), "192.168.80.102", true, false, true, "untagged", null, null, null, 100, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, "10.3.96.1"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, "3954"); + + IpAssocAnswer answer = (IpAssocAnswer) _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test // remove public & private interface details, source nat, guest network + public void shutdownGuestNetwork() throws ConfigurationException, ExecutionException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: shutdownGuestNetwork"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + IpAddressTO ip = new IpAddressTO(Long.valueOf("1"), "192.168.80.102", false, false, true, "untagged", null, null, null, 100, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, "10.3.96.1"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, "3954"); + + IpAssocAnswer answer = (IpAssocAnswer) _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addIngressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addIngressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + List cidrList = new ArrayList(); + cidrList.add("0.0.0.0/0"); + FirewallRuleTO active = new FirewallRuleTO(8, + null, "192.168.80.103", "tcp", 80, 80, false, false, + FirewallRule.Purpose.Firewall, cidrList, null, null); + rules.add(active); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removeIngressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removeIngressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_ingress_fw_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + FirewallRuleTO revoked = new FirewallRuleTO(8, + null, "192.168.80.103", "tcp", 80, 80, true, false, + FirewallRule.Purpose.Firewall, null, null, null); + rules.add(revoked); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addEgressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addEgressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + List cidrList = new ArrayList(); + cidrList.add("0.0.0.0/0"); + FirewallRuleVO activeVO = new FirewallRuleVO(null, null, 80, 80, "tcp", + 1, 1, 1, Purpose.Firewall, cidrList, null, + null, null, FirewallRule.TrafficType.Egress); + FirewallRuleTO active = new FirewallRuleTO(activeVO, Long.toString(vlanId), null, Purpose.Firewall, FirewallRule.TrafficType.Egress); + rules.add(active); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removeEgressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removeEgressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_egress_fw_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + FirewallRuleVO revokedVO = new FirewallRuleVO(null, null, 80, 80, "tcp", + 1, 1, 1, Purpose.Firewall, null, null, null, null, FirewallRule.TrafficType.Egress); + revokedVO.setState(State.Revoke); + FirewallRuleTO revoked = new FirewallRuleTO(revokedVO, Long.toString(vlanId), null, Purpose.Firewall, FirewallRule.TrafficType.Egress); + rules.add(revoked); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addStaticNatRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addStaticNatRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + StaticNatRuleTO active = new StaticNatRuleTO(0, "192.168.80.103", null, + null, "10.3.97.158", null, null, null, false, false); + rules.add(active); + + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rules, null); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removeStaticNatRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removeStaticNatRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_stc_nat_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + StaticNatRuleTO revoked = new StaticNatRuleTO(0, "192.168.80.103", null, + null, "10.3.97.158", null, null, null, true, false); + rules.add(revoked); + + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rules, null); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addPortForwardingRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addPortForwardingRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + PortForwardingRuleTO active = new PortForwardingRuleTO(9, "192.168.80.103", 80, + 80, "10.3.97.158", 8080, 8080, "tcp", false, false); + rules.add(active); + + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removePortForwardingRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removePortForwardingRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_dst_nat_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + PortForwardingRuleTO revoked = new PortForwardingRuleTO(9, "192.168.80.103", 80, + 80, "10.3.97.158", 8080, 8080, "tcp", true, false); + rules.add(revoked); + + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } +} + diff --git a/plugins/pom.xml b/plugins/pom.xml index 4f193bcde0c..d0817a2ddc8 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -44,6 +44,7 @@ network-elements/elastic-loadbalancer network-elements/ovs network-elements/juniper-contrail + network-elements/palo-alto network-elements/nicira-nvp network-elements/bigswitch-vns network-elements/midonet diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 769d34562c3..36ef4bd9dc0 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -2631,7 +2631,7 @@ public class ApiResponseHelper implements ResponseGenerator { List serviceProvidersResponses = new ArrayList(); for (Network.Provider serviceProvider : serviceProviders) { // return only Virtual Router/JuniperSRX/CiscoVnmc as a provider for the firewall - if (service == Service.Firewall && !(serviceProvider == Provider.VirtualRouter || serviceProvider == Provider.JuniperSRX || serviceProvider == Provider.CiscoVnmc)) { + if (service == Service.Firewall && !(serviceProvider == Provider.VirtualRouter || serviceProvider == Provider.JuniperSRX || serviceProvider == Provider.CiscoVnmc || serviceProvider == Provider.PaloAlto)) { continue; } diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index e3aa4fa7617..2e9b3889dfb 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3792,6 +3792,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati firewallProvider = provider; } + if (provider == Provider.PaloAlto) { + firewallProvider = Provider.PaloAlto; + } + if ((service == Service.PortForwarding || service == Service.StaticNat) && provider == Provider.VirtualRouter) { firewallProvider = Provider.VirtualRouter; diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index 85f24c6aa43..8bd547bf3ce 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -322,6 +322,7 @@ dictionary = { 'label.add.new.gateway': '', 'label.add.new.NetScaler': '', 'label.add.new.SRX': '', +'label.add.new.PA': '', 'label.add.new.tier': '', 'label.add.NiciraNvp.device': '', 'label.add.pod': '', @@ -334,6 +335,7 @@ dictionary = { 'label.add.security.group': '', 'label.add.service.offering': '', 'label.add.SRX.device': '', +'label.add.PA.device': '', 'label.add.static.nat.rule': '', 'label.add.static.route': '', 'label.add.system.service.offering': '', @@ -480,6 +482,7 @@ dictionary = { 'label.delete.NiciraNvp': '', 'label.delete.project': '', 'label.delete.SRX': '', +'label.delete.PA': '', 'label.delete.VPN.connection': '', 'label.delete.VPN.customer.gateway': '', 'label.delete.VPN.gateway': '', @@ -859,6 +862,8 @@ dictionary = { 'label.owned.public.ips': '', 'label.owner.account': '', 'label.owner.domain': '', +'label.PA.log.profile': '', +'label.PA.threat.profile': '', 'label.parent.domain': '', 'label.password.enabled': '', 'label.password': '', @@ -1031,6 +1036,7 @@ dictionary = { 'label.specify.vxlan': '', 'label.SR.name ': '', 'label.srx': '', +'label.PA': '', 'label.start.IP': '', 'label.start.port': '', 'label.start.reserved.system.IP': '', @@ -1332,6 +1338,7 @@ dictionary = { 'message.confirm.delete.F5': '', 'message.confirm.delete.NetScaler': '', 'message.confirm.delete.SRX': '', +'message.confirm.delete.PA': '', 'message.confirm.destroy.router': '', 'message.confirm.disable.provider': '', 'message.confirm.enable.provider': '', diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index 3a4f8ca604f..7b7edf47bc4 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -770,6 +770,75 @@ cloudStack.docs = { desc: 'Number of guest networks/accounts that will share this device', externalLink: '' }, + // Add Palo Alto + helpPaloAltoIPAddress: { + desc: 'The IP address of the device', + externalLink: '' + }, + helpPaloAltoUsername: { + desc: 'A user ID with valid authentication credentials that provide to access the device', + externalLink: '' + }, + helpPaloAltoPassword: { + desc: 'The password for the user ID provided in Username', + externalLink: '' + }, + helpPaloAltoType: { + desc: 'The type of device that is being added', + externalLink: '' + }, + helpPaloAltoPublicInterface: { + desc: 'Interface of device that is configured to be part of the public network. For example, ge-0/0/2', + externalLink: '' + }, + helpPaloAltoPrivateInterface: { + desc: 'Interface of device that is configured to be part of the private network. For example, ge-0/0/1', + externalLink: '' + }, + helpPaloAltoUsageInterface: { + desc: 'Interface used to meter traffic. If you don\'t want to use the public interface, specify a different interface name here.', + externalLink: '' + }, + helpPaloAltoRetries: { + desc: 'Number of times to attempt a command on the device before considering the operation failed. Default is 2.', + externalLink: '' + }, + helpPaloAltoTimeout: { + desc: 'The time to wait for a command on the Palo Alto before considering it failed. Default is 300 seconds.', + externalLink: '' + }, + helpPaloAltoMode: { + desc: 'Side by side mode is supported for the Palo Alto.', + externalLink: '' + }, + helpPaloAltoPublicNetwork: { + desc: 'The name of the public network on the Palo Alto. For example, trust.', + externalLink: '' + }, + helpPaloAltoPrivateNetwork: { + desc: 'The name of the private network on the Palo Alto. For example, untrust.', + externalLink: '' + }, + helpPaloAltoVirtualRouter: { + desc: 'The name of the virtual router on the Palo Alto.', + externalLink: '' + }, + helpPaloAltoThreatProfile: { + desc: 'The threat profile name/group to associate with allow firewall policies.', + externalLink: '' + }, + helpPaloAltoLogProfile: { + desc: 'The log profile name/group to associate with allow firewall policies.', + externalLink: '' + }, + helpPaloAltoDedicated: { + desc: 'Check this box to dedicate the device to a single account. The value in the Capacity field will be ignored.', + externalLink: '' + }, + helpPaloAltoCapacity: { + desc: 'Number of guest networks/accounts that will share this device', + externalLink: '' + }, // Add system service offering helpSystemOfferingName: { desc: 'Any desired name for the offering', diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 1579d16c74d..fa22811cd8b 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -4944,6 +4944,288 @@ } }, + // Palo Alto provider detailView + pa: { + type: 'detailView', + id: 'paProvider', + label: 'label.PA', + viewAll: { + label: 'label.devices', + path: '_zone.paDevices' + }, + tabs: { + details: { + title: 'label.details', + fields: [{ + name: { + label: 'label.name' + } + }, { + state: { + label: 'label.state' + } + }], + dataProvider: function (args) { + refreshNspData("PaloAlto"); + var providerObj; + $(nspHardcodingArray).each(function () { + if (this.id == "pa") { + providerObj = this; + return false; //break each loop + } + }); + args.response.success({ + data: providerObj, + actionFilter: networkProviderActionFilter('pa') + }); + } + } + }, + actions: { + add: { + label: 'label.add.PA.device', + createForm: { + title: 'label.add.PA.device', + fields: { + ip: { + label: 'label.ip.address', + docID: 'helpPaloAltoIPAddress' + }, + username: { + label: 'label.username', + docID: 'helpPaloAltoUsername' + }, + password: { + label: 'label.password', + isPassword: true, + docID: 'helpPaloAltoPassword' + }, + networkdevicetype: { + label: 'label.type', + docID: 'helpPaloAltoType', + select: function (args) { + var items = []; + items.push({ + id: "PaloAltoFirewall", + description: "Palo Alto Firewall" + }); + args.response.success({ + data: items + }); + } + }, + publicinterface: { + label: 'label.public.interface', + docID: 'helpPaloAltoPublicInterface' + }, + privateinterface: { + label: 'label.private.interface', + docID: 'helpPaloAltoPrivateInterface' + }, + //usageinterface: { + // label: 'Usage interface', + // docID: 'helpPaloAltoUsageInterface' + //}, + numretries: { + label: 'label.numretries', + defaultValue: '2', + docID: 'helpPaloAltoRetries' + }, + timeout: { + label: 'label.timeout', + defaultValue: '300', + docID: 'helpPaloAltoTimeout' + }, + // inline: { + // label: 'Mode', + // docID: 'helpPaloAltoMode', + // select: function(args) { + // var items = []; + // items.push({id: "false", description: "side by side"}); + // items.push({id: "true", description: "inline"}); + // args.response.success({data: items}); + // } + // }, + publicnetwork: { + label: 'label.public.network', + defaultValue: 'untrust', + docID: 'helpPaloAltoPublicNetwork' + }, + privatenetwork: { + label: 'label.private.network', + defaultValue: 'trust', + docID: 'helpPaloAltoPrivateNetwork' + }, + pavr: { + label: 'label.virtual.router', + docID: 'helpPaloAltoVirtualRouter' + }, + patp: { + label: 'label.PA.threat.profile', + docID: 'helpPaloAltoThreatProfile' + }, + palp: { + label: 'label.PA.log.profile', + docID: 'helpPaloAltoLogProfile' + }, + capacity: { + label: 'label.capacity', + validation: { + required: false, + number: true + }, + docID: 'helpPaloAltoCapacity' + }, + dedicated: { + label: 'label.dedicated', + isBoolean: true, + isChecked: false, + docID: 'helpPaloAltoDedicated' + } + } + }, + action: function (args) { + if (nspMap["pa"] == null) { + $.ajax({ + url: createURL("addNetworkServiceProvider&name=PaloAlto&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + dataType: "json", + async: true, + success: function (json) { + var jobId = json.addnetworkserviceproviderresponse.jobid; + var addPaloAltoProviderIntervalID = setInterval(function () { + $.ajax({ + url: createURL("queryAsyncJobResult&jobId=" + jobId), + dataType: "json", + success: function (json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } else { + clearInterval(addPaloAltoProviderIntervalID); + if (result.jobstatus == 1) { + nspMap["pa"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider; + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } else if (result.jobstatus == 2) { + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + _s(result.jobresult.errortext)); + } + } + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + errorMsg); + } + }); + }, 3000); + } + }); + } else { + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } + }, + messages: { + notification: function (args) { + return 'label.add.PA.device'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + enable: { + label: 'label.enable.provider', + action: function (args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["pa"].id + "&state=Enabled"), + dataType: "json", + success: function (json) { + var jid = json.updatenetworkserviceproviderresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function (json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + } + }); + }, + messages: { + confirm: function (args) { + return 'message.confirm.enable.provider'; + }, + notification: function () { + return 'label.enable.provider'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + disable: { + label: 'label.disable.provider', + action: function (args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["pa"].id + "&state=Disabled"), + dataType: "json", + success: function (json) { + var jid = json.updatenetworkserviceproviderresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function (json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + } + }); + }, + messages: { + confirm: function (args) { + return 'message.confirm.disable.provider'; + }, + notification: function () { + return 'label.disable.provider'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + destroy: { + label: 'label.shutdown.provider', + action: function (args) { + $.ajax({ + url: createURL("deleteNetworkServiceProvider&id=" + nspMap["pa"].id), + dataType: "json", + success: function (json) { + var jid = json.deletenetworkserviceproviderresponse.jobid; + args.response.success({ + _custom: { + jobId: jid + } + }); + + $(window).trigger('cloudStack.fullRefresh'); + } + }); + }, + messages: { + confirm: function (args) { + return 'message.confirm.shutdown.provider'; + }, + notification: function (args) { + return 'label.shutdown.provider'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + } + }, + // Security groups detail view securityGroups: { id: 'securityGroup-providers', @@ -9156,6 +9438,250 @@ } } }, + + //Palo Alto devices listView + paDevices: { + id: 'paDevices', + title: 'label.devices', + listView: { + id: 'paDevices', + fields: { + ipaddress: { + label: 'label.ip.address' + }, + fwdevicestate: { + label: 'label.status' + }, + fwdevicename: { + label: 'label.type' + } + }, + actions: { + add: { + label: 'label.add.PA.device', + createForm: { + title: 'label.add.PA.device', + fields: { + ip: { + label: 'label.ip.address' + }, + username: { + label: 'label.username' + }, + password: { + label: 'label.password', + isPassword: true + }, + networkdevicetype: { + label: 'label.type', + select: function (args) { + var items = []; + items.push({ + id: "PaloAltoFirewall", + description: "Palo Alto Firewall" + }); + args.response.success({ + data: items + }); + } + }, + publicinterface: { + label: 'label.public.interface' + }, + privateinterface: { + label: 'label.private.interface' + }, + //usageinterface: { + // label: 'label.usage.interface' + //}, + numretries: { + label: 'label.numretries', + defaultValue: '2' + }, + timeout: { + label: 'label.timeout', + defaultValue: '300' + }, + // inline: { + // label: 'Mode', + // select: function(args) { + // var items = []; + // items.push({id: "false", description: "side by side"}); + // items.push({id: "true", description: "inline"}); + // args.response.success({data: items}); + // } + // }, + publicnetwork: { + label: 'label.public.network', + defaultValue: 'untrust' + }, + privatenetwork: { + label: 'label.private.network', + defaultValue: 'trust' + }, + pavr: { + label: 'label.virtual.router' + }, + patp: { + label: 'label.PA.threat.profile' + }, + palp: { + label: 'label.PA.log.profile' + }, + capacity: { + label: 'label.capacity', + validation: { + required: false, + number: true + } + }, + dedicated: { + label: 'label.dedicated', + isBoolean: true, + isChecked: false + } + } + }, + action: function (args) { + if (nspMap["pa"] == null) { + $.ajax({ + url: createURL("addNetworkServiceProvider&name=PaloAlto&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + dataType: "json", + async: true, + success: function (json) { + var jobId = json.addnetworkserviceproviderresponse.jobid; + var addPaloAltoProviderIntervalID = setInterval(function () { + $.ajax({ + url: createURL("queryAsyncJobResult&jobId=" + jobId), + dataType: "json", + success: function (json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } else { + clearInterval(addPaloAltoProviderIntervalID); + if (result.jobstatus == 1) { + nspMap["pa"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider; + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } else if (result.jobstatus == 2) { + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + _s(result.jobresult.errortext)); + } + } + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + errorMsg); + } + }); + }, 3000); + } + }); + } else { + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } + }, + messages: { + notification: function (args) { + return 'label.add.PA.device'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + dataProvider: function (args) { + $.ajax({ + url: createURL("listPaloAltoFirewalls&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + data: { + page: args.page, + pageSize: pageSize + }, + dataType: "json", + async: false, + success: function (json) { + var items = json.listpaloaltofirewallresponse.paloaltofirewall; + args.response.success({ + data: items + }); + } + }); + }, + detailView: { + name: 'Palo Alto details', + actions: { + 'remove': { + label: 'label.delete.PA', + messages: { + confirm: function (args) { + return 'message.confirm.delete.PA'; + }, + notification: function (args) { + return 'label.delete.PA'; + } + }, + action: function (args) { + $.ajax({ + url: createURL("deletePaloAltoFirewall&fwdeviceid=" + args.context.paDevices[0].fwdeviceid), + dataType: "json", + async: true, + success: function (json) { + var jid = json.deletepaloaltofirewallresponse.jobid; + args.response.success({ + _custom: { + jobId: jid + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + tabs: { + details: { + title: 'label.details', + fields: [{ + fwdeviceid: { + label: 'label.id' + }, + ipaddress: { + label: 'label.ip.address' + }, + fwdevicestate: { + label: 'label.status' + }, + fwdevicename: { + label: 'label.type' + }, + fwdevicecapacity: { + label: 'label.capacity' + }, + timeout: { + label: 'label.timeout' + } + }], + dataProvider: function (args) { + $.ajax({ + url: createURL("listPaloAltoFirewalls&fwdeviceid=" + args.context.paDevices[0].fwdeviceid), + dataType: "json", + async: true, + success: function (json) { + var item = json.listpaloaltofirewallresponse.paloaltofirewall[0]; + args.response.success({ + data: item + }); + } + }); + } + } + } + } + } + }, + // FIXME convert to nicira detailview // NiciraNvp devices listView niciraNvpDevices: { @@ -15763,6 +16289,44 @@ } url.push("fwdevicededicated=" + dedicated.toString()); + // START - Palo Alto Specific Fields + var externalVirtualRouter = args.data.pavr; + if(externalVirtualRouter != null && externalVirtualRouter.length > 0) { + if(isQuestionMarkAdded == false) { + url.push("?"); + isQuestionMarkAdded = true; + } + else { + url.push("&"); + } + url.push("pavr=" + encodeURIComponent(externalVirtualRouter)); + } + + var externalThreatProfile = args.data.patp; + if(externalThreatProfile != null && externalThreatProfile.length > 0) { + if(isQuestionMarkAdded == false) { + url.push("?"); + isQuestionMarkAdded = true; + } + else { + url.push("&"); + } + url.push("patp=" + encodeURIComponent(externalThreatProfile)); + } + + var externalLogProfile = args.data.palp; + if(externalLogProfile != null && externalLogProfile.length > 0) { + if(isQuestionMarkAdded == false) { + url.push("?"); + isQuestionMarkAdded = true; + } + else { + url.push("&"); + } + url.push("palp=" + encodeURIComponent(externalLogProfile)); + } + // END - Palo Alto Specific Fields + array1.push("&url=" + todb(url.join(""))); //construct URL ends here @@ -16495,6 +17059,9 @@ case "JuniperSRX": nspMap["srx"] = items[i]; break; + case "PaloAlto": + nspMap["pa"] = items[i]; + break; case "SecurityGroupProvider": nspMap["securityGroups"] = items[i]; break; @@ -16576,6 +17143,11 @@ name: 'SRX', state: nspMap.srx ? nspMap.srx.state : 'Disabled' }); + nspHardcodingArray.push({ + id: 'pa', + name: 'Palo Alto', + state: nspMap.pa ? nspMap.pa.state : 'Disabled' + }); } };