From 9db7112bab9cc8fe3517e3ba83dc3dc7146a63b2 Mon Sep 17 00:00:00 2001 From: Filip Chmielewski Date: Fri, 25 May 2018 18:58:35 +0200 Subject: [PATCH] Add firewall rule workflows Change-Id: I879b19931afdd88c875af88bf75ab6df70b6b8ce Partial-Bug: #1772594 --- .../buildNumber.properties | 4 +- .../net/juniper/contrail/vro/config/Config.kt | 15 +- .../constants/ModelSpecificConstants.kt | 18 ++ .../juniper/contrail/vro/model/Executor.kt | 3 +- .../vro/model/FirewallRulePropertyExecutor.kt | 82 ++++++++ .../net/juniper/contrail/vro/model/Utils.kt | 37 +++- .../vro/format/DisplayNameFormatter.kt | 12 ++ .../contrail/vro/format/PropertyFormatter.kt | 19 +- .../src/main/resources/templates/finders.ftl | 2 + .../vro/tests/workflows/Dependencies.kt | 8 + .../main/js/workflows/createFirewallRule.js | 41 ++++ .../src/main/js/workflows/editFirewallRule.js | 30 +++ .../vro/workflows/custom/FirewallRule.kt | 195 ++++++++++++++++++ 13 files changed, 448 insertions(+), 18 deletions(-) create mode 100644 o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/ModelSpecificConstants.kt create mode 100644 o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/FirewallRulePropertyExecutor.kt create mode 100644 o11nplugin-contrail-workflows/src/main/js/workflows/createFirewallRule.js create mode 100644 o11nplugin-contrail-workflows/src/main/js/workflows/editFirewallRule.js create mode 100644 o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt diff --git a/o11nplugin-contrail-config/buildNumber.properties b/o11nplugin-contrail-config/buildNumber.properties index 71b44200..ef695efb 100644 --- a/o11nplugin-contrail-config/buildNumber.properties +++ b/o11nplugin-contrail-config/buildNumber.properties @@ -1,3 +1,3 @@ #maven.buildNumber.plugin properties file -#Fri May 18 13:04:09 CEST 2018 -buildNumber=1354 +#Wed Jun 06 17:32:42 CEST 2018 +buildNumber=1423 diff --git a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Config.kt b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Config.kt index 5daad0f1..03ca3e65 100644 --- a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Config.kt +++ b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/Config.kt @@ -76,7 +76,8 @@ val customCreateWorkflows = setOf( the(), the(), the(), - the() + the(), + the() ) val customEditWorkflows = setOf( @@ -86,7 +87,8 @@ val customEditWorkflows = setOf( the(), the(), the(), - the() + the(), + the() ) val customDeleteWorkflows = setOf( @@ -133,7 +135,10 @@ val hiddenRelations = setOf( pair(), pair(), pair(), - pair() + pair(), + pair(), + pair(), + pair() ) val tagRelations = setOf( @@ -152,6 +157,10 @@ val reversedRelations = setOf( pair() ) +val readUponQuery = setOf( + the() +) + private inline fun the() = T::class.java.simpleName diff --git a/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/ModelSpecificConstants.kt b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/ModelSpecificConstants.kt new file mode 100644 index 00000000..e30027a0 --- /dev/null +++ b/o11nplugin-contrail-config/src/main/kotlin/net/juniper/contrail/vro/config/constants/ModelSpecificConstants.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2018 Juniper Networks, Inc. All rights reserved. + */ + +package net.juniper.contrail.vro.config.constants + +enum class EndpointType(val value: String) { + None("none"), + Tag("tag"), + AddressGroup("address group"), + VirtualNetwork("virtual network"), + AnyWorkload("any workload") +} + +enum class ServiceType(val value: String) { + Manual("manual"), + Reference("reference") +} \ No newline at end of file diff --git a/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Executor.kt b/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Executor.kt index 953f685d..fabeebc5 100644 --- a/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Executor.kt +++ b/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Executor.kt @@ -17,7 +17,8 @@ import net.juniper.contrail.api.types.TagType // They are combined using inheritance so that their functions become Executor's methods. class Executor(private val connection: Connection) : SecurityGroupRuleProperties by SecurityGroupRulePropertyExecutor(connection), -NetworkPolicyRuleProperties by NetworkPolicyRulePropertyExecutor(connection) { +NetworkPolicyRuleProperties by NetworkPolicyRulePropertyExecutor(connection), +FirewallRuleComplexProperties by FirewallRuleComplexPropertyExecutor(connection) { fun VirtualNetwork.subnets(): List { val ipams = networkIpam ?: return emptyList() return ipams.asSequence().map { it.attr.ipamSubnets.asSequence().filterNotNull() }.flatten().toList() diff --git a/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/FirewallRulePropertyExecutor.kt b/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/FirewallRulePropertyExecutor.kt new file mode 100644 index 00000000..8a0d4622 --- /dev/null +++ b/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/FirewallRulePropertyExecutor.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Juniper Networks, Inc. All rights reserved. + */ + +package net.juniper.contrail.vro.model + +import net.juniper.contrail.api.types.AddressGroup +import net.juniper.contrail.api.types.FirewallRule +import net.juniper.contrail.api.types.FirewallRuleEndpointType +import net.juniper.contrail.api.types.Tag +import net.juniper.contrail.api.types.VirtualNetwork +import net.juniper.contrail.vro.config.constants.EndpointType +import net.juniper.contrail.vro.config.constants.ServiceType + +interface FirewallRuleComplexProperties { + fun FirewallRule.endpointType(endpointNumber: Int): String? + fun FirewallRule.serviceType(): String? + fun FirewallRule.endpointNetwork(endpointNumber: Int): VirtualNetwork? + fun FirewallRule.endpointAddressGroup(endpointNumber: Int): AddressGroup? + fun FirewallRule.endpointTags(endpointNumber: Int): List? + fun FirewallRule.serviceSrcPorts(): String? + fun FirewallRule.serviceDstPorts(): String? +} + +open class FirewallRuleComplexPropertyExecutor(private val connection: Connection) : FirewallRuleComplexProperties { + override fun FirewallRule.endpointType(endpointNumber: Int): String? = + endpoint(endpointNumber)?.type() + + override fun FirewallRule.serviceType(): String? = when { + serviceGroup[0] != null -> ServiceType.Reference.value + else -> ServiceType.Manual.value + } + + override fun FirewallRule.endpointNetwork(endpointNumber: Int): VirtualNetwork? { + val networkFqn = endpoint(endpointNumber)?.virtualNetwork ?: return null + return connection.findByFQN(networkFqn) + } + + override fun FirewallRule.endpointAddressGroup(endpointNumber: Int): AddressGroup? { + val addressGroupFqn = endpoint(endpointNumber)?.addressGroup ?: return null + return connection.findByFQN(addressGroupFqn) + } + + override fun FirewallRule.serviceSrcPorts(): String? = + utils.formatPort(service.srcPorts) + + override fun FirewallRule.serviceDstPorts(): String? = + utils.formatPort(service.dstPorts) + + override fun FirewallRule.endpointTags(endpointNumber: Int): List? { + val rawTags = endpoint(endpointNumber)?.tags ?: return null + return rawTags.map { + tagFromString(qualifiedName, it) + } + } + + private fun tagFromString(ruleFqn: List, tagString: String): Tag? { + val projectFqn = ruleFqn.dropLast(1) + val isGlobal = tagString.split(":")[0] == "global" + val tagFqn = if (isGlobal) { + tagString.split(":")[1] + } else { + val fqnList = projectFqn + tagString + fqnList.joinToString(":") + } + return connection.findByFQN(tagFqn) + } + + private fun FirewallRuleEndpointType.type(): String? = when { + any == true -> EndpointType.AnyWorkload.value + addressGroup != null -> EndpointType.AddressGroup.value + virtualNetwork != null -> EndpointType.VirtualNetwork.value + tags.isNotEmpty() -> EndpointType.Tag.value + else -> EndpointType.None.value + } + + private fun FirewallRule.endpoint(endpointNumber: Int): FirewallRuleEndpointType? = when (endpointNumber) { + 1 -> endpoint1 + 2 -> endpoint2 + else -> null + } +} \ No newline at end of file diff --git a/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Utils.kt b/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Utils.kt index 78310fad..610e8d5a 100644 --- a/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Utils.kt +++ b/o11nplugin-contrail-core/src/main/kotlin/net/juniper/contrail/vro/model/Utils.kt @@ -6,22 +6,25 @@ package net.juniper.contrail.vro.model import net.juniper.contrail.api.types.AddressGroup import net.juniper.contrail.api.types.AddressType +import net.juniper.contrail.api.types.AllowedAddressPair +import net.juniper.contrail.api.types.FirewallRuleEndpointType +import net.juniper.contrail.api.types.FirewallServiceType +import net.juniper.contrail.api.types.IpamSubnetType import net.juniper.contrail.api.types.NetworkIpam import net.juniper.contrail.api.types.NetworkPolicy import net.juniper.contrail.api.types.PolicyRuleType import net.juniper.contrail.api.types.PortType import net.juniper.contrail.api.types.RouteType import net.juniper.contrail.api.types.SecurityGroup +import net.juniper.contrail.api.types.ServiceGroup import net.juniper.contrail.api.types.SubnetType +import net.juniper.contrail.api.types.Tag import net.juniper.contrail.api.types.VirtualNetwork import net.juniper.contrail.api.types.VnSubnetsType -import net.juniper.contrail.api.types.AllowedAddressPair -import net.juniper.contrail.api.types.FirewallServiceType -import net.juniper.contrail.api.types.IpamSubnetType -import net.juniper.contrail.api.types.ServiceGroup import net.juniper.contrail.vro.base.Description -import net.juniper.contrail.vro.config.constants.minPort +import net.juniper.contrail.vro.config.constants.EndpointType import net.juniper.contrail.vro.config.constants.maxPort +import net.juniper.contrail.vro.config.constants.minPort import net.juniper.contrail.vro.format.PropertyFormatter import java.util.UUID @@ -152,6 +155,9 @@ class Utils { return ranges.map { parsePortRange(it, anyAsFullRange = true) } } + fun parsePortsOfFirewallRule(ports: String): PortType = + parsePortRange(ports, true) + fun formatPort(port: PortType): String = if (port.startPort == port.endPort) if (port.startPort == -1) "any" else "${port.startPort}" @@ -244,6 +250,22 @@ class Utils { return AddressType(subnet, networkName, securityGroupName, policyName) } + fun createEndpoint( + type: String, + tags: List?, + virtualNetwork: VirtualNetwork?, + addressGroup: AddressGroup? + ): FirewallRuleEndpointType { + val anyWorkload = type == EndpointType.AnyWorkload.value + val tagNames = if (type == EndpointType.Tag.value && tags != null) tags.map { tagToString(it) } else listOf() + val virtualNetworkFqn = virtualNetwork?.qualifiedName?.joinToString(":") + val addressGroupFqn = addressGroup?.qualifiedName?.joinToString(":") + return FirewallRuleEndpointType(null, virtualNetworkFqn, addressGroupFqn, tagNames, null, anyWorkload) + } + + fun tagToString(tag: Tag): String = + if (tag.parentType == "project") tag.name else "global:${tag.name}" + fun randomUUID(): String = UUID.randomUUID().toString() @@ -284,9 +306,8 @@ class Utils { "community-attributes ${route.communityAttributes?.communityAttribute?.joinToString(",") ?: ""}" } - fun routeStringToIndex(routeString: String): Int { - return routeString.split(":")[0].toInt() - } + fun routeStringToIndex(routeString: String): Int = + routeString.split(":")[0].toInt() fun subnetToString(subnet: SubnetType?): String? { if (subnet == null) return null diff --git a/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/DisplayNameFormatter.kt b/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/DisplayNameFormatter.kt index 9450e075..47131888 100644 --- a/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/DisplayNameFormatter.kt +++ b/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/DisplayNameFormatter.kt @@ -5,6 +5,7 @@ package net.juniper.contrail.vro.format import net.juniper.contrail.api.ApiObjectBase +import net.juniper.contrail.api.types.FirewallRule import net.juniper.contrail.api.types.FloatingIp import net.juniper.contrail.api.types.IpamSubnetType import net.juniper.contrail.api.types.QuotaType @@ -29,4 +30,15 @@ object DisplayNameFormatter { fun format(obj: QuotaType): String? = "Quotas" + + fun format(obj: FirewallRule): String? { + // `obj.parent?.name` returns null, so we use `obj.qualifiedName.dropLast(1).last()` to get the parent name. + val parentName = obj.qualifiedName.dropLast(1).last() + val simpleAction = obj.actionList?.simpleAction + val direction = obj.direction + val service = if (obj.service != null) PropertyFormatter.format(obj.service) else obj.serviceGroup[0].referredName.last() + val endpoint1 = PropertyFormatter.format(obj.endpoint1) + val endpoint2 = PropertyFormatter.format(obj.endpoint2) + return "$parentName: $simpleAction $service EP1: $endpoint1 $direction EP2: $endpoint2" + } } \ No newline at end of file diff --git a/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/PropertyFormatter.kt b/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/PropertyFormatter.kt index 091cf0cc..9abe1ae9 100644 --- a/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/PropertyFormatter.kt +++ b/o11nplugin-contrail-format/src/main/kotlin/net/juniper/contrail/vro/format/PropertyFormatter.kt @@ -49,6 +49,21 @@ object PropertyFormatter { } } + fun format(prop: FirewallRuleEndpointType): String = prop.run { + when { + subnet != null -> format(subnet) + virtualNetwork != null -> "VN:$virtualNetwork" + addressGroup != null -> "AG:$addressGroup" + any == true -> "ANY" + tags != null && tags.isNotEmpty() -> "Tags:${tags.joinToString(",")}" + else -> "-" + } + } + + fun format(prop: FirewallServiceType): String = prop.run { + "$protocol:${format(srcPorts)}:${format(dstPorts)}" + } + fun format(prop: ShareType) = "${prop.tenant}: ${prop.tenantAccess.formatAccess()}" @@ -75,10 +90,6 @@ object PropertyFormatter { "${actionList.safeSimpleAction}$protocol ${srcAddresses.inline} ${srcPorts.inline} $direction ${dstAddresses.inline} ${dstPorts.inline}" } - fun format(prop: FirewallServiceType) = prop.run { - "$protocol:${format(srcPorts)}:${format(dstPorts)}" - } - private val ActionListType?.safeSimpleAction get() = if (this == null) "" else "$simpleAction " diff --git a/o11nplugin-contrail-generator/src/main/resources/templates/finders.ftl b/o11nplugin-contrail-generator/src/main/resources/templates/finders.ftl index 7542b344..255137a1 100644 --- a/o11nplugin-contrail-generator/src/main/resources/templates/finders.ftl +++ b/o11nplugin-contrail-generator/src/main/resources/templates/finders.ftl @@ -7,6 +7,7 @@ import com.vmware.o11n.sdk.modeldriven.PluginContext import com.vmware.o11n.sdk.modeldriven.Sid import org.springframework.beans.factory.annotation.Autowired import net.juniper.contrail.vro.base.ConnectionRepository +import net.juniper.contrail.vro.config.readUponQuery import net.juniper.contrail.vro.model.Connection import net.juniper.contrail.api.* // ktlint-disable no-wildcard-imports import net.juniper.contrail.api.types.* // ktlint-disable no-wildcard-imports @@ -17,6 +18,7 @@ private fun ConnectionRepository.query(clazz: Class, quer private fun Connection.query(clazz: Class, query: String, key: String): List>? = list(clazz)?.asSequence() ?.filter { query.isBlank() || it.name.startsWith(query) } + ?.onEach { if (readUponQuery.contains(clazz.simpleName)) read(it) } ?.map { FoundObject(it, info.sid.with(key, it.uuid)) } ?.toList() diff --git a/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt b/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt index 681009d1..421c3bfc 100644 --- a/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt +++ b/o11nplugin-contrail-tests/src/test/kotlin/net/juniper/contrail/vro/tests/workflows/Dependencies.kt @@ -6,6 +6,7 @@ package net.juniper.contrail.vro.tests.workflows import net.juniper.contrail.vro.gen.AddressGroup_Wrapper import net.juniper.contrail.vro.gen.Connection_Wrapper +import net.juniper.contrail.vro.gen.FirewallRule_Wrapper import net.juniper.contrail.vro.gen.FloatingIpPool_Wrapper import net.juniper.contrail.vro.gen.FloatingIp_Wrapper import net.juniper.contrail.vro.gen.IpamSubnetType_Wrapper @@ -111,6 +112,13 @@ class Dependencies(private val connection: Connection_Wrapper, private val utils setParentServiceInstance(parent) } + @JvmOverloads + fun someFirewallRule(parent: Project_Wrapper = someProject()) = FirewallRule_Wrapper().apply { + uuid = randomStringUuid() + name = "someFirewallRule$uuid" + setParentProject(parent) + } + fun someServiceTemplate() = ServiceTemplate_Wrapper().apply { uuid = randomStringUuid() name = "someServiceTemplate$uuid" diff --git a/o11nplugin-contrail-workflows/src/main/js/workflows/createFirewallRule.js b/o11nplugin-contrail-workflows/src/main/js/workflows/createFirewallRule.js new file mode 100644 index 00000000..217eea23 --- /dev/null +++ b/o11nplugin-contrail-workflows/src/main/js/workflows/createFirewallRule.js @@ -0,0 +1,41 @@ +var rule = new ContrailFirewallRule(); +var ruleUuid = ContrailUtils.randomUUID(); +rule.setUuid(ruleUuid); +rule.setName(ruleUuid); +if (typeof parentProject === 'undefined') { + // if the parent project is undefined, we're using the policy management version + var defaultPolicyManagement = parentConnection.findPolicyManagementByFQName("default-policy-management"); + rule.setParentPolicyManagement(defaultPolicyManagement); +} else { + rule.setParentProject(parentProject); +} +rule.setDirection(direction); + +var actions = new ContrailActionListType() +actions.setSimpleAction(action); +rule.setActionList(actions); + +var endpoint1 = ContrailUtils.createEndpoint(endpoint1Type, endpoint1Tags, endpoint1VirtualNetwork, endpoint1AddressGroup); +var endpoint2 = ContrailUtils.createEndpoint(endpoint2Type, endpoint2Tags, endpoint2VirtualNetwork, endpoint2AddressGroup); +rule.setEndpoint1(endpoint1); +rule.setEndpoint2(endpoint2); + +var mTags = new ContrailFirewallRuleMatchTagsType([]); +if(matchTags != null) { + mTags = new ContrailFirewallRuleMatchTagsType(matchTags); +} +rule.setMatchTags(mTags); + +if(serviceType == "manual") { + var service = new ContrailFirewallServiceType(); + service.setProtocol(serviceProtocol); + service.setSrcPorts(ContrailUtils.parsePortsOfFirewallRule(serviceSrcPorts)); + service.setDstPorts(ContrailUtils.parsePortsOfFirewallRule(serviceDstPorts)); + rule.setService(service); +} else { + if(serviceReference != null) { + rule.addServiceGroup(serviceReference); + } +} + +rule.create(); \ No newline at end of file diff --git a/o11nplugin-contrail-workflows/src/main/js/workflows/editFirewallRule.js b/o11nplugin-contrail-workflows/src/main/js/workflows/editFirewallRule.js new file mode 100644 index 00000000..535f4bbf --- /dev/null +++ b/o11nplugin-contrail-workflows/src/main/js/workflows/editFirewallRule.js @@ -0,0 +1,30 @@ +rule.setDirection(direction); + +var actions = new ContrailActionListType() +actions.setSimpleAction(action); +rule.setActionList(actions); + +var endpoint1 = ContrailUtils.createEndpoint(endpoint1Type, endpoint1Tags, endpoint1VirtualNetwork, endpoint1AddressGroup) +var endpoint2 = ContrailUtils.createEndpoint(endpoint2Type, endpoint2Tags, endpoint2VirtualNetwork, endpoint2AddressGroup) +rule.setEndpoint1(endpoint1) +rule.setEndpoint2(endpoint2) + +var mTags = new ContrailFirewallRuleMatchTagsType([]); +if(matchTags != null) { + mTags = new ContrailFirewallRuleMatchTagsType(matchTags); +} +rule.setMatchTags(mTags); + +if(serviceType == "manual") { + var service = new ContrailFirewallServiceType(); + service.setProtocol(serviceProtocol); + service.setSrcPorts(ContrailUtils.parsePortsOfFirewallRule(serviceSrcPorts)); + service.setDstPorts(ContrailUtils.parsePortsOfFirewallRule(serviceDstPorts)); + rule.setService(service); +} else { + if(serviceReference != null) { + rule.addServiceGroup(serviceReference); + } +} + +rule.update(); \ No newline at end of file diff --git a/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt new file mode 100644 index 00000000..d8871bcf --- /dev/null +++ b/o11nplugin-contrail-workflows/src/main/kotlin/net/juniper/contrail/vro/workflows/custom/FirewallRule.kt @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2018 Juniper Networks, Inc. All rights reserved. + */ + +package net.juniper.contrail.vro.workflows.custom + +import net.juniper.contrail.api.types.ActionListType +import net.juniper.contrail.api.types.AddressGroup +import net.juniper.contrail.api.types.FirewallRule +import net.juniper.contrail.api.types.FirewallRuleEndpointType +import net.juniper.contrail.api.types.FirewallServiceType +import net.juniper.contrail.api.types.Project +import net.juniper.contrail.api.types.ServiceGroup +import net.juniper.contrail.api.types.Tag +import net.juniper.contrail.api.types.VirtualNetwork +import net.juniper.contrail.vro.config.constants.Connection +import net.juniper.contrail.vro.config.constants.EndpointType +import net.juniper.contrail.vro.config.constants.ServiceType +import net.juniper.contrail.vro.schema.Schema +import net.juniper.contrail.vro.schema.propertyDescription +import net.juniper.contrail.vro.schema.simpleTypeConstraints +import net.juniper.contrail.vro.workflows.dsl.BasicParameterBuilder +import net.juniper.contrail.vro.workflows.dsl.FromComplexPropertyValue +import net.juniper.contrail.vro.workflows.dsl.FromStringParameter +import net.juniper.contrail.vro.workflows.dsl.ParameterAggregator +import net.juniper.contrail.vro.workflows.dsl.PresentationParametersBuilder +import net.juniper.contrail.vro.workflows.dsl.WhenNonNull +import net.juniper.contrail.vro.workflows.dsl.WorkflowDefinition +import net.juniper.contrail.vro.workflows.model.array +import net.juniper.contrail.vro.workflows.model.reference +import net.juniper.contrail.vro.workflows.model.string + +val defaultEndpointType = EndpointType.None.value +val allowedEndpointTypes = EndpointType.values().map { it.value } + +const val serviceTypeParameterName = "serviceType" +val defaultServiceType = ServiceType.Manual.value +val allowedServiceTypes = ServiceType.values().map { it.value } + +val allowedMatchTags = listOf("application", "tier", "deployment", "site") + +val parentConnectionField = "parentConnection" +val parentProjectField = "parentProject" + +internal fun createPolicyManagementFirewallRule(schema: Schema): WorkflowDefinition { + + val workflowName = "Create global firewall rule" + + return customWorkflow(workflowName).withScriptFile("createFirewallRule") { + step("Parent") { + parameter(parentConnectionField, Connection.reference) { + description = "Contrail connection in which the rule will be created" + mandatory = true + } + } + output("rule", reference()) { + description = "Rule created by this workflow" + } + firewallRuleParameters(schema, parentConnectionField, false) + } +} + +internal fun createProjectFirewallRule(schema: Schema): WorkflowDefinition { + + val workflowName = "Create firewall rule in project" + + return customWorkflow(workflowName).withScriptFile("createFirewallRule") { + step("Parent") { + parameter(parentProjectField, reference()) { + description = "Project this firewall rule will belong to" + mandatory = true + } + } + output("rule", reference()) { + description = "Rule created by this workflow" + } + firewallRuleParameters(schema, parentProjectField, false) + } +} + +internal fun editFirewallRule(schema: Schema): WorkflowDefinition { + + val workflowName = "Edit firewall rule" + + return customWorkflow(workflowName).withScriptFile("editFirewallRule") { + step("Rule") { + parameter("rule", reference()) { + mandatory = true + } + } + firewallRuleParameters(schema, "rule", true) + + } +} + +private fun PresentationParametersBuilder.firewallRuleParameters(schema: Schema, visibilityDependencyField: String, loadCurrentValues: Boolean) { + step("Basic attributes") { + visibility = WhenNonNull(visibilityDependencyField) + parameter("action", string) { + description = schema.propertyDescription("simpleAction") + additionalQualifiers += schema.simpleTypeConstraints("simpleAction") + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("actionList.simpleAction") + } + parameter("direction", string) { + description = "Direction" + mandatory = true + additionalQualifiers += schema.simpleTypeConstraints("direction") + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("direction") + } + } + step("Endpoints") { + visibility = WhenNonNull(visibilityDependencyField) + endpointParameters(schema, 1, loadCurrentValues) + endpointParameters(schema, 2, loadCurrentValues) + } + step("Service") { + visibility = WhenNonNull(visibilityDependencyField) + parameter(serviceTypeParameterName, string) { + mandatory = true + predefinedAnswers = allowedServiceTypes + defaultValue = defaultServiceType + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceType()") + } + parameter("serviceProtocol", string) { + description = schema.propertyDescription("protocol") + defaultValue = "any" + visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Manual.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("service.protocol") + } + parameter("serviceSrcPorts", string) { + description = schema.propertyDescription("src-ports") + defaultValue = "any" + visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Manual.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceSrcPorts()") + } + parameter("serviceDstPorts", string) { + description = schema.propertyDescription("dst-ports") + defaultValue = "any" + visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Manual.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceDstPorts()") + } + parameter("serviceReference", reference()) { + description = "Service group" + visibility = FromStringParameter(serviceTypeParameterName, ServiceType.Reference.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("serviceGroup[0]") + } + } + step("Match Tags") { + visibility = WhenNonNull(visibilityDependencyField) + parameter("matchTags", array(string)) { + predefinedAnswers = allowedMatchTags + sameValues = false + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("matchTags.tagList") + } + } +} + +private fun ParameterAggregator.endpointParameters(schema: Schema, endpointNumber: Int, loadCurrentValues: Boolean) { + val endpointName = endpointParameterName(endpointNumber) + val endpointTypeParameterName = "${endpointName}Type" + parameter(endpointTypeParameterName, string) { + description = "End Point $endpointNumber type" + mandatory = true + predefinedAnswers = allowedEndpointTypes + defaultValue = defaultEndpointType + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointType($endpointNumber)") + } + parameter("${endpointName}Tags", array(reference())) { + description = schema.propertyDescription("tags") + visibility = FromStringParameter(endpointTypeParameterName, EndpointType.Tag.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointTags($endpointNumber)") + } + parameter("${endpointName}VirtualNetwork", reference()) { + description = schema.propertyDescription("virtual-network") + visibility = FromStringParameter(endpointTypeParameterName, EndpointType.VirtualNetwork.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointNetwork($endpointNumber)") + } + parameter("${endpointName}AddressGroup", reference()) { + description = schema.propertyDescription("address-group") + visibility = FromStringParameter(endpointTypeParameterName, EndpointType.AddressGroup.value) + mandatory = true + if (loadCurrentValues) dataBinding = firewallRulePropertyDataBinding("endpointAddressGroup($endpointNumber)") + } +} + +private fun endpointParameterName(endpointNumber: Int) = "endpoint$endpointNumber" + +private fun BasicParameterBuilder.firewallRulePropertyDataBinding(path: String) = + FromComplexPropertyValue("rule", path, type)