From: Martin Sunal Date: Sat, 28 Mar 2015 04:01:53 +0000 (+0100) Subject: Introduced neutron-mapper X-Git-Tag: release/lithium~111 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=1f4b7eb95cde0b9095256e8298ceb272d501135f;p=groupbasedpolicy.git Introduced neutron-mapper - dependecy on ODL neutron-northbound service - Mapping between neutron and GBP entities: - neutron network to gbp l2-flood-domain mapping l2-bridge-domain is generated as a parent of l2-flood-domain l3-context is generated as a parent of l2-bridge-domain - neutron subnet to gbp subnet mapping l2-flood domain is set as a parent of subnet - neutron port to gbp endpoint mapping endpoint is registered endpoint-l3 is registered if a port contains fixed-ips - neutron security group with security group rules to endpoint group pairs with contract Change-Id: I1fea5cb67ebd7d080cf244ecc2b1da5072efd181 Signed-off-by: Martin Sunal --- diff --git a/commons/parent/pom.xml b/commons/parent/pom.xml index f48dadef7..54558fca3 100644 --- a/commons/parent/pom.xml +++ b/commons/parent/pom.xml @@ -23,12 +23,14 @@ 0.1.0-SNAPSHOT 0.1.0-SNAPSHOT 0.6.0-SNAPSHOT + 0.5.0-SNAPSHOT 1.2.0-SNAPSHOT etc/opendaylight/karaf 0.2.0-SNAPSHOT 15-groupbasedpolicy-ofoverlay.xml 15-groupbasedpolicy-opflex.xml 15-groupbasedpolicy-openstackendpoint.xml + 15-neutron-mapper.xml 3.0.1 4.4.0 0.1.0-SNAPSHOT diff --git a/features/pom.xml b/features/pom.xml index 0fdd10865..fccd52e13 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -54,6 +54,13 @@ features xml + + org.opendaylight.neutron + features-neutron + features + ${neutron.version} + xml + org.opendaylight.groupbasedpolicy groupbasedpolicy @@ -89,6 +96,13 @@ xml config + + org.opendaylight.groupbasedpolicy + neutron-mapper-config + ${project.version} + xml + config + org.opendaylight.controller diff --git a/features/src/main/resources/features.xml b/features/src/main/resources/features.xml index 776244351..6f1122a60 100644 --- a/features/src/main/resources/features.xml +++ b/features/src/main/resources/features.xml @@ -19,6 +19,9 @@ mvn:org.opendaylight.openflowplugin/features-openflowplugin/${openflowplugin.distribution.version}/xml/features mvn:org.opendaylight.openflowplugin/features-openflowplugin-extension/${openflowplugin.distribution.version}/xml/features + + mvn:org.opendaylight.neutron/features-neutron/${neutron.version}/xml/features + odl-mdsal-broker @@ -78,5 +81,17 @@ mvn:org.opendaylight.groupbasedpolicy/groupbasedpolicy-openstackendpoint-config/${project.version}/xml/config + + + + odl-mdsal-broker + odl-neutron-service + odl-groupbasedpolicy-base + odl-groupbasedpolicy-ofoverlay + mvn:org.opendaylight.groupbasedpolicy/neutron-mapper/${project.version} + mvn:org.opendaylight.groupbasedpolicy/neutron-mapper-config/${project.version}/xml/config + diff --git a/neutron-mapper-config/pom.xml b/neutron-mapper-config/pom.xml new file mode 100644 index 000000000..1ed430fb1 --- /dev/null +++ b/neutron-mapper-config/pom.xml @@ -0,0 +1,48 @@ + + + + + 4.0.0 + + org.opendaylight.groupbasedpolicy + commons.groupbasedpolicy + 0.2.0-SNAPSHOT + ../commons/parent + + + neutron-mapper-config + Controller Configuration files for neutron-mapper + jar + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + + attach-artifact + + package + + + + ${project.build.directory}/classes/initial/15-neutron-mapper.xml + xml + config + + + + + + + + + diff --git a/neutron-mapper-config/src/main/resources/initial/15-neutron-mapper.xml b/neutron-mapper-config/src/main/resources/initial/15-neutron-mapper.xml new file mode 100644 index 000000000..2ba6576cf --- /dev/null +++ b/neutron-mapper-config/src/main/resources/initial/15-neutron-mapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + neutron-mapper:neutron-mapper-impl + + neutron-mapper-impl + + + binding:binding-rpc-registry + binding-rpc-broker + + + + binding:binding-async-data-broker + binding-data-broker + + + + + + + + + urn:opendaylight:params:xml:ns:yang:controller:config:neutron-mapper:impl?module=neutron-mapper-impl&revision=2015-02-19 + + + diff --git a/neutron-mapper/pom.xml b/neutron-mapper/pom.xml new file mode 100644 index 000000000..0921f0c17 --- /dev/null +++ b/neutron-mapper/pom.xml @@ -0,0 +1,124 @@ + + + + 4.0.0 + + org.opendaylight.groupbasedpolicy + groupbasedpolicy.project + 0.2.0-SNAPSHOT + ../ + + neutron-mapper + bundle + + + + ${project.groupId} + groupbasedpolicy + ${project.version} + + + ${project.groupId} + ofoverlay-renderer + ${project.version} + + + org.opendaylight.neutron + neutron-spi + 0.5.0-SNAPSHOT + + + org.opendaylight.controller + config-api + + + org.opendaylight.controller + sal-binding-api + + + org.opendaylight.controller + sal-binding-config + + + org.osgi + org.osgi.core + + + + + + + org.apache.felix + maven-bundle-plugin + + + + * + + + + + org.opendaylight.yangtools + yang-maven-plugin + + + config + + generate-sources + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + ${project.build.directory}/generated-sources/config + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + ${project.build.directory}/generated-sources/sal + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + 0.3.0-SNAPSHOT + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + add-source + + generate-sources + + + target/generated-sources/sal + target/generated-sources/config + target/generated-resources/ + + + + + + + + diff --git a/neutron-mapper/src/main/java/org/opendaylight/controller/config/yang/config/neutron_mapper/impl/NeutronMapperModule.java b/neutron-mapper/src/main/java/org/opendaylight/controller/config/yang/config/neutron_mapper/impl/NeutronMapperModule.java new file mode 100644 index 000000000..e48646b70 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/controller/config/yang/config/neutron_mapper/impl/NeutronMapperModule.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.yang.config.neutron_mapper.impl; + +import org.opendaylight.groupbasedpolicy.neutron.mapper.NeutronMapper; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NeutronMapperModule extends org.opendaylight.controller.config.yang.config.neutron_mapper.impl.AbstractNeutronMapperModule { + + private final Logger LOG = LoggerFactory.getLogger(NeutronMapperModule.class); + private BundleContext bundleContext; + + public NeutronMapperModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public NeutronMapperModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.neutron_mapper.impl.NeutronMapperModule oldModule, java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + final NeutronMapper neutronMapper = new NeutronMapper(getDataBrokerDependency(), getRpcRegistryDependency(), bundleContext); + LOG.info("Neutron mapper started."); + return new AutoCloseable() { + + @Override + public void close() throws Exception { + neutronMapper.close(); + } + }; + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/controller/config/yang/config/neutron_mapper/impl/NeutronMapperModuleFactory.java b/neutron-mapper/src/main/java/org/opendaylight/controller/config/yang/config/neutron_mapper/impl/NeutronMapperModuleFactory.java new file mode 100644 index 000000000..e1fc85ef8 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/controller/config/yang/config/neutron_mapper/impl/NeutronMapperModuleFactory.java @@ -0,0 +1,48 @@ +/* +* Generated file +* +* Generated from: yang module name: neutron-mapper-impl yang module local name: neutron-mapper-impl +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Thu Feb 19 12:58:22 CET 2015 +* +* Do not modify this file unless it is present under src/main directory +*/ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.yang.config.neutron_mapper.impl; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; +import org.opendaylight.controller.config.spi.Module; +import org.osgi.framework.BundleContext; + +public class NeutronMapperModuleFactory extends org.opendaylight.controller.config.yang.config.neutron_mapper.impl.AbstractNeutronMapperModuleFactory { + + /** + * @see org.opendaylight.controller.config.yang.config.neutron_mapper.impl.AbstractNeutronMapperModuleFactory#createModule(java.lang.String, org.opendaylight.controller.config.api.DependencyResolver, org.osgi.framework.BundleContext) + */ + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, BundleContext bundleContext) { + NeutronMapperModule module = (NeutronMapperModule) super.createModule(instanceName, dependencyResolver, bundleContext); + module.setBundleContext(bundleContext); + return module; + } + + /** + * @see org.opendaylight.controller.config.yang.config.neutron_mapper.impl.AbstractNeutronMapperModuleFactory#createModule(java.lang.String, org.opendaylight.controller.config.api.DependencyResolver, org.opendaylight.controller.config.api.DynamicMBeanWithInstance, org.osgi.framework.BundleContext) + */ + @Override + public Module createModule(String instanceName, DependencyResolver dependencyResolver, DynamicMBeanWithInstance old, + BundleContext bundleContext) throws Exception { + NeutronMapperModule module = (NeutronMapperModule) super.createModule(instanceName, dependencyResolver, old, bundleContext); + module.setBundleContext(bundleContext); + return module; + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/NeutronMapper.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/NeutronMapper.java new file mode 100644 index 000000000..7306a0491 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/NeutronMapper.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.groupbasedpolicy.neutron.mapper; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronNetworkAware; +import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronPortAware; +import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronSecurityGroupAware; +import org.opendaylight.groupbasedpolicy.neutron.mapper.mapping.NeutronSubnetAware; +import org.opendaylight.neutron.spi.INeutronNetworkAware; +import org.opendaylight.neutron.spi.INeutronPortAware; +import org.opendaylight.neutron.spi.INeutronSecurityGroupAware; +import org.opendaylight.neutron.spi.INeutronSubnetAware; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +public class NeutronMapper implements AutoCloseable { + + private final List> registrations = new ArrayList>(); + + public NeutronMapper(DataBroker dataProvider, RpcProviderRegistry rpcProvider, BundleContext context) { + checkNotNull(dataProvider); + checkNotNull(rpcProvider); + checkNotNull(context); + EndpointService epService = rpcProvider.getRpcService(EndpointService.class); + + registerAwareProviders(dataProvider, epService, context); + } + + private void registerAwareProviders(DataBroker dataProvider, EndpointService epService, BundleContext context) { + ServiceRegistration neutronNetworkAwareRegistration = context.registerService( + INeutronNetworkAware.class, new NeutronNetworkAware(dataProvider), null); + registrations.add(neutronNetworkAwareRegistration); + + ServiceRegistration neutronSubnetAwareRegistration = context.registerService( + INeutronSubnetAware.class, new NeutronSubnetAware(dataProvider), null); + registrations.add(neutronSubnetAwareRegistration); + + ServiceRegistration neutronPortAwareRegistration = context.registerService( + INeutronPortAware.class, new NeutronPortAware(dataProvider, epService), null); + registrations.add(neutronPortAwareRegistration); + + ServiceRegistration neutronSecurityGroupAwareRegistration = context.registerService( + INeutronSecurityGroupAware.class, new NeutronSecurityGroupAware(dataProvider), null); + registrations.add(neutronSecurityGroupAwareRegistration); + } + + /** + * @see java.lang.AutoCloseable#close() + */ + @Override + public void close() throws Exception { + for (ServiceRegistration registration : registrations) { + registration.unregister(); + } + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronNetworkAware.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronNetworkAware.java new file mode 100644 index 000000000..e59e2bdda --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronNetworkAware.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.UUID; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils; +import org.opendaylight.neutron.spi.INeutronNetworkAware; +import org.opendaylight.neutron.spi.NeutronNetwork; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMappingBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup.IntraGroupPolicy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroupBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomainBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomainBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3ContextBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class NeutronNetworkAware implements INeutronNetworkAware { + + private static final Logger LOG = LoggerFactory.getLogger(NeutronNetworkAware.class); + private final DataBroker dataProvider; + + public NeutronNetworkAware(DataBroker dataProvider) { + this.dataProvider = checkNotNull(dataProvider); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronNetworkAware#canCreateNetwork(org.opendaylight.neutron.spi.NeutronNetwork) + */ + @Override + public int canCreateNetwork(NeutronNetwork network) { + LOG.trace("canCreateNetwork - {}", network); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronNetworkAware#neutronNetworkCreated(org.opendaylight.neutron.spi.NeutronNetwork) + */ + @Override + public void neutronNetworkCreated(NeutronNetwork network) { + LOG.trace("neutronNetworkCreated - {}", network); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + L2FloodDomainId l2FdId = new L2FloodDomainId(network.getID()); + TenantId tenantId = new TenantId(Utils.normalizeUuid(network.getTenantID())); + addEpgDhcpIfMissing(tenantId, rwTx); + addEpgRouterIfMissing(tenantId, rwTx); + Description domainDescription = new Description(MappingUtils.NEUTRON_NETWORK__ + network.getID()); + Name name = null; + if (network.getNetworkName() != null) { + name = new Name(network.getNetworkName()); + } + L3ContextId l3ContextId = new L3ContextId(UUID.randomUUID().toString()); + L3Context l3Context = new L3ContextBuilder().setId(l3ContextId) + .setDescription(domainDescription) + .setName(name) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l3ContextIid(tenantId, l3ContextId), l3Context, true); + + L2BridgeDomainId l2BdId = new L2BridgeDomainId(UUID.randomUUID().toString()); + L2BridgeDomain l2Bd = new L2BridgeDomainBuilder().setId(l2BdId) + .setParent(l3ContextId) + .setDescription(domainDescription) + .setName(name) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BdId), l2Bd, true); + + L2FloodDomain l2Fd = new L2FloodDomainBuilder().setId(l2FdId) + .setParent(l2BdId) + .setDescription(domainDescription) + .setName(name) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2FloodDomainIid(tenantId, l2FdId), l2Fd, true); + + NetworkMapping networkMapping = new NetworkMappingBuilder().setNetworkId(l2FdId) + .setL2BridgeDomainId(l2BdId) + .setL3ContextId(l3ContextId) + .build(); + rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.networkMappingIid(l2FdId), networkMapping, true); + + DataStoreHelper.submitToDs(rwTx); + } + + private void addEpgDhcpIfMissing(TenantId tenantId, ReadWriteTransaction rwTx) { + InstanceIdentifier epgDhcpIid = IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_DHCP_ID); + Optional potentialDhcpEpg = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + epgDhcpIid, rwTx); + if (!potentialDhcpEpg.isPresent()) { + EndpointGroup epgDhcp = new EndpointGroupBuilder().setId(MappingUtils.EPG_DHCP_ID) + .setName(new Name("DHCP_group")) + .setDescription(new Description("Group where are all DHCP endpoints.")) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, epgDhcpIid, epgDhcp); + } + } + + private void addEpgRouterIfMissing(TenantId tenantId, ReadWriteTransaction rwTx) { + Optional potentialEpgRouter = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_ROUTER_ID), rwTx); + if (potentialEpgRouter.isPresent()) { + EndpointGroup epgRouter = new EndpointGroupBuilder().setId(MappingUtils.EPG_ROUTER_ID) + .setDescription(new Description(MappingUtils.NEUTRON_ROUTER__ + "epg_routers")) + .setIntraGroupPolicy(IntraGroupPolicy.RequireContract) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, + IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_ROUTER_ID), epgRouter); + } + } + + /** + * @see org.opendaylight.neutron.spi.INeutronNetworkAware#canUpdateNetwork(org.opendaylight.neutron.spi.NeutronNetwork, + * org.opendaylight.neutron.spi.NeutronNetwork) + */ + @Override + public int canUpdateNetwork(NeutronNetwork delta, NeutronNetwork original) { + LOG.trace("canUpdateNetwork - delta: {} original: {}", delta, original); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronNetworkAware#neutronNetworkUpdated(org.opendaylight.neutron.spi.NeutronNetwork) + */ + @Override + public void neutronNetworkUpdated(NeutronNetwork network) { + LOG.trace("neutronNetworkUpdated - {}", network); + // TODO we could update just name + } + + /** + * @see org.opendaylight.neutron.spi.INeutronNetworkAware#canDeleteNetwork(org.opendaylight.neutron.spi.NeutronNetwork) + */ + @Override + public int canDeleteNetwork(NeutronNetwork network) { + LOG.trace("canDeleteNetwork - {}", network); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronNetworkAware#neutronNetworkDeleted(org.opendaylight.neutron.spi.NeutronNetwork) + */ + @Override + public void neutronNetworkDeleted(NeutronNetwork network) { + LOG.trace("neutronNetworkDeleted - {}", network); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(network.getTenantID())); + L2FloodDomainId l2FdId = new L2FloodDomainId(network.getID()); + Optional potentionalNetworkMapping = DataStoreHelper.readFromDs( + LogicalDatastoreType.OPERATIONAL, IidFactory.networkMappingIid(l2FdId), rwTx); + if (!potentionalNetworkMapping.isPresent()) { + LOG.warn("Illegal state - network-mapping {} does not exist.", l2FdId.getValue()); + rwTx.cancel(); + return; + } + + NetworkMapping networkMapping = potentionalNetworkMapping.get(); + L2BridgeDomainId l2BdId = networkMapping.getL2BridgeDomainId(); + L3ContextId l3ContextId = networkMapping.getL3ContextId(); + if (l2BdId == null || l3ContextId == null) { + LOG.warn("Illegal state - network-mapping {} is not valid.", networkMapping); + rwTx.cancel(); + return; + } + + Optional potentialL2Fd = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.l2FloodDomainIid(tenantId, l2FdId), rwTx); + if (!potentialL2Fd.isPresent()) { + LOG.warn("Illegal state - l2-flood-domain {} does not exist.", l2FdId.getValue()); + rwTx.cancel(); + return; + } + + Optional potentialL2Bd = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.l2BridgeDomainIid(tenantId, l2BdId), rwTx); + if (!potentialL2Bd.isPresent()) { + LOG.warn("Illegal state - l2-bridge-domain {} does not exist.", l2BdId.getValue()); + rwTx.cancel(); + return; + } + + Optional potentialL3Context = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.l3ContextIid(tenantId, l3ContextId), rwTx); + if (!potentialL3Context.isPresent()) { + LOG.warn("Illegal state - l3-context {} does not exist.", l3ContextId.getValue()); + rwTx.cancel(); + return; + } + + DataStoreHelper.submitToDs(rwTx); + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronPortAware.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronPortAware.java new file mode 100644 index 000000000..95945a600 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronPortAware.java @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils.ForwardingCtx; +import org.opendaylight.neutron.spi.INeutronPortAware; +import org.opendaylight.neutron.spi.NeutronPort; +import org.opendaylight.neutron.spi.NeutronSecurityGroup; +import org.opendaylight.neutron.spi.NeutronSecurityRule; +import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.RegisterEndpointInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.UnregisterEndpointInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3AddressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L2Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContextInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; + +public class NeutronPortAware implements INeutronPortAware { + + private static final Logger LOG = LoggerFactory.getLogger(NeutronPortAware.class); + private static final String DEVICE_OWNER_DHCP = "network:dhcp"; + private static final String DEVICE_OWNER_ROUTER_IFACE = "network:router_interface"; + private static final int DHCP_CLIENT_PORT = 68; + private static final int DHCP_SERVER_PORT = 67; + private final DataBroker dataProvider; + private final EndpointService epService; + + public NeutronPortAware(DataBroker dataProvider, EndpointService epService) { + this.dataProvider = checkNotNull(dataProvider); + this.epService = checkNotNull(epService); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronPortAware#canCreatePort(org.opendaylight.neutron.spi.NeutronPort) + */ + @Override + public int canCreatePort(NeutronPort port) { + LOG.trace("canCreatePort - {}", port); + // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints. + List fixedIPs = port.getFixedIPs(); + if (fixedIPs != null && fixedIPs.size() > 1) { + LOG.warn("Neutron mapper does not support multiple IPs on the same port."); + return StatusCode.BAD_REQUEST; + } + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronPortAware#neutronPortCreated(org.opendaylight.neutron.spi.NeutronPort) + */ + @Override + public void neutronPortCreated(NeutronPort port) { + LOG.trace("neutronPortCreated - {}", port); + if (isRouterInterfacePort(port)) { + LOG.trace("Port is router interface - do nothing - NeutronRouterAware handles router iface"); + return; + } + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID())); + + if (isDhcpPort(port)) { + List dhcpSecRules = createDhcpSecRules(port, null, rwTx); + if (dhcpSecRules == null) { + rwTx.cancel(); + return; + } + + for (NeutronSecurityRule dhcpSecRule : dhcpSecRules) { + boolean isDhcpSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(dhcpSecRule, rwTx); + if (!isDhcpSecRuleAdded) { + rwTx.cancel(); + return; + } + } + } else { + List secGroups = port.getSecurityGroups(); + if (secGroups != null) { + for (NeutronSecurityGroup secGroup : secGroups) { + EndpointGroupId epgId = new EndpointGroupId(secGroup.getSecurityGroupUUID()); + Optional potentialEpg = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.endpointGroupIid(tenantId, epgId), rwTx); + if (!potentialEpg.isPresent()) { + boolean isSecGroupCreated = NeutronSecurityGroupAware.addNeutronSecurityGroup(secGroup, rwTx); + if (!isSecGroupCreated) { + rwTx.cancel(); + return; + } + if (containsSecRuleWithRemoteSecGroup(secGroup)) { + List dhcpSecRules = createDhcpSecRules(port, epgId, rwTx); + if (dhcpSecRules == null) { + rwTx.cancel(); + return; + } + List routerSecRules = NeutronRouterAware.createRouterSecRules(port, epgId, rwTx); + if (routerSecRules == null) { + rwTx.cancel(); + return; + } + } + } else { + List secRules = secGroup.getSecurityRules(); + if (secRules != null) { + for (NeutronSecurityRule secRule : secRules) { + NeutronSecurityRuleAware.addNeutronSecurityRule(secRule, rwTx); + } + } + } + } + } + } + + boolean isNeutronPortCreated = addNeutronPort(port, rwTx, epService); + if (!isNeutronPortCreated) { + rwTx.cancel(); + return; + } + + DataStoreHelper.submitToDs(rwTx); + } + + public static boolean addNeutronPort(NeutronPort port, ReadWriteTransaction rwTx, EndpointService epService) { + TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID())); + L2FloodDomainId l2FdId = new L2FloodDomainId(port.getNetworkUUID()); + ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx); + boolean isFwCtxValid = validateForwardingCtx(fwCtx); + if (!isFwCtxValid) { + return false; + } + + try { + RegisterEndpointInput registerEpRpcInput = createRegisterEndpointInput(port, fwCtx); + RpcResult rpcResult = epService.registerEndpoint(registerEpRpcInput).get(); + if (!rpcResult.isSuccessful()) { + LOG.warn("Illegal state - RPC registerEndpoint failed. Input of RPC: {}", registerEpRpcInput); + return false; + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("addPort - RPC invocation failed.", e); + return false; + } + return true; + } + + private static boolean validateForwardingCtx(ForwardingCtx fwCtx) { + if (fwCtx.getL2FloodDomain() == null) { + LOG.warn("Illegal state - l2-flood-domain does not exist."); + return false; + } + if (fwCtx.getL2BridgeDomain() == null) { + LOG.warn("Illegal state - l2-bridge-domain does not exist."); + return false; + } + if (fwCtx.getL3Context() == null) { + LOG.warn("Illegal state - l3-context does not exist."); + return false; + } + return true; + } + + private List createDhcpSecRules(NeutronPort port, EndpointGroupId consumerEpgId, ReadTransaction rTx) { + TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID())); + Neutron_IPs firstIp = getFirstIp(port.getFixedIPs()); + if (firstIp == null) { + LOG.warn("Illegal state - DHCP port does not have an IP address."); + return null; + } + SubnetId dhcpSubnetId = new SubnetId(firstIp.getSubnetUUID()); + Optional potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.subnetIid(tenantId, dhcpSubnetId), rTx); + if (!potentialSubnet.isPresent()) { + LOG.warn("Illegal state - Subnet {} where is DHCP port does not exist.", dhcpSubnetId.getValue()); + return null; + } + IpPrefix ipSubnet = potentialSubnet.get().getIpPrefix(); + NeutronSecurityRule dhcpRuleEgress = createDhcpSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId, true); + NeutronSecurityRule dhcpRuleIngress = createDhcpSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId, false); + return ImmutableList.of(dhcpRuleEgress, dhcpRuleIngress); + } + + private NeutronSecurityRule createDhcpSecRule(String ruleUuid, TenantId tenantId, IpPrefix ipSubnet, EndpointGroupId consumerEpgId, + boolean isEgress) { + NeutronSecurityRule dhcpSecRule = new NeutronSecurityRule(); + dhcpSecRule.setSecurityRuleGroupID(MappingUtils.EPG_DHCP_ID.getValue()); + dhcpSecRule.setSecurityRuleTenantID(tenantId.getValue()); + dhcpSecRule.setSecurityRuleRemoteIpPrefix(Utils.getStringIpPrefix(ipSubnet)); + if (consumerEpgId != null) { + dhcpSecRule.setSecurityRemoteGroupID(consumerEpgId.getValue()); + } + if (isEgress) { + dhcpSecRule.setSecurityRuleUUID(NeutronUtils.EGRESS + "__" + ruleUuid); + dhcpSecRule.setSecurityRuleDirection(NeutronUtils.EGRESS); + dhcpSecRule.setSecurityRulePortMin(DHCP_CLIENT_PORT); + dhcpSecRule.setSecurityRulePortMax(DHCP_CLIENT_PORT); + } else { + dhcpSecRule.setSecurityRuleUUID(NeutronUtils.INGRESS + "__" + ruleUuid); + dhcpSecRule.setSecurityRuleDirection(NeutronUtils.INGRESS); + dhcpSecRule.setSecurityRulePortMin(DHCP_SERVER_PORT); + dhcpSecRule.setSecurityRulePortMax(DHCP_SERVER_PORT); + } + dhcpSecRule.setSecurityRuleProtocol(NeutronUtils.UDP); + if (ipSubnet.getIpv4Prefix() != null) { + dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv4); + } else { + dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv6); + } + return dhcpSecRule; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronPortAware#canUpdatePort(org.opendaylight.neutron.spi.NeutronPort, + * org.opendaylight.neutron.spi.NeutronPort) + */ + @Override + public int canUpdatePort(NeutronPort delta, NeutronPort original) { + LOG.trace("canUpdatePort - delta: {} original: {}", delta, original); + if (delta.getFixedIPs() == null || delta.getFixedIPs().isEmpty()) { + return StatusCode.OK; + } + // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints. + List fixedIPs = delta.getFixedIPs(); + if (fixedIPs != null && fixedIPs.size() > 1) { + LOG.warn("Neutron mapper does not support multiple IPs on the same port."); + return StatusCode.BAD_REQUEST; + } + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronPortAware#neutronPortUpdated(org.opendaylight.neutron.spi.NeutronPort) + */ + @Override + public void neutronPortUpdated(NeutronPort port) { + LOG.trace("neutronPortUpdated - {}", port); + if (isRouterInterfacePort(port)) { + LOG.trace("Port is router interface - do nothing - NeutronRouterAware handles router iface"); + return; + } + ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID())); + MacAddress macAddress = new MacAddress(port.getMacAddress()); + L2FloodDomainId l2FdId = new L2FloodDomainId(port.getNetworkUUID()); + ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rTx); + boolean isFwCtxValid = validateForwardingCtx(fwCtx); + if (!isFwCtxValid) { + rTx.close(); + return; + } + + Optional potentionalEp = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, + IidFactory.endpointIid(fwCtx.getL2BridgeDomain().getId(), macAddress), rTx); + if (!potentionalEp.isPresent()) { + LOG.warn("Illegal state - endpoint {} does not exist.", new EndpointKey(fwCtx.getL2BridgeDomain().getId(), + macAddress)); + rTx.close(); + return; + } + + Endpoint ep = potentionalEp.get(); + if (isEpIpDifferentThanPortFixedIp(ep, port) || isEpgDifferentThanSecGrp(ep, port)) { + UnregisterEndpointInput unregisterEpRpcInput = createUnregisterEndpointInput(ep); + RegisterEndpointInput registerEpRpcInput = createRegisterEndpointInput(port, fwCtx); + try { + RpcResult rpcResult = epService.unregisterEndpoint(unregisterEpRpcInput).get(); + if (!rpcResult.isSuccessful()) { + LOG.warn("Illegal state - RPC unregisterEndpoint failed. Input of RPC: {}", unregisterEpRpcInput); + rTx.close(); + return; + } + rpcResult = epService.registerEndpoint(registerEpRpcInput).get(); + if (!rpcResult.isSuccessful()) { + LOG.warn("Illegal state - RPC registerEndpoint failed. Input of RPC: {}", registerEpRpcInput); + rTx.close(); + return; + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("addPort - RPC invocation failed.", e); + rTx.close(); + return; + } + } + rTx.close(); + } + + private boolean isEpIpDifferentThanPortFixedIp(Endpoint ep, NeutronPort port) { + List l3Addresses = ep.getL3Address(); + List fixedIPs = port.getFixedIPs(); + if ((l3Addresses == null || l3Addresses.isEmpty()) && (fixedIPs == null || fixedIPs.isEmpty())) { + return false; + } + if (l3Addresses != null && !l3Addresses.isEmpty() && fixedIPs != null && !fixedIPs.isEmpty()) { + if (fixedIPs.get(0).getIpAddress().equals(Utils.getStringIpAddress(l3Addresses.get(0).getIpAddress()))) { + return false; + } + } + return true; + } + + private boolean isEpgDifferentThanSecGrp(Endpoint ep, NeutronPort port) { + List epgIds = ep.getEndpointGroups(); + List secGroups = port.getSecurityGroups(); + if ((epgIds == null || epgIds.isEmpty()) && (secGroups == null || secGroups.isEmpty())) { + return false; + } + if (epgIds != null && !epgIds.isEmpty() && secGroups != null && !secGroups.isEmpty()) { + if (epgIds.size() != secGroups.size()) { + return true; + } + Collection epgIdsFromSecGroups = Collections2.transform(secGroups, + new Function() { + + @Override + public EndpointGroupId apply(NeutronSecurityGroup input) { + return new EndpointGroupId(input.getSecurityGroupUUID()); + } + }); + // order independent equals + Set one = new HashSet<>(epgIds); + Set two = new HashSet<>(epgIdsFromSecGroups); + if (one.equals(two)) { + return false; + } + } + return true; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronPortAware#canDeletePort(org.opendaylight.neutron.spi.NeutronPort) + */ + @Override + public int canDeletePort(NeutronPort port) { + LOG.trace("canDeletePort - {}", port); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronPortAware#neutronPortDeleted(org.opendaylight.neutron.spi.NeutronPort) + */ + @Override + public void neutronPortDeleted(NeutronPort port) { + LOG.trace("neutronPortDeleted - {}", port); + if (isRouterInterfacePort(port)) { + LOG.trace("Port is router interface - do nothing - NeutronRouterAware handles router iface"); + return; + } + ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID())); + L2FloodDomainId l2FdId = new L2FloodDomainId(port.getNetworkUUID()); + ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rTx); + boolean isFwCtxValid = validateForwardingCtx(fwCtx); + if (!isFwCtxValid) { + rTx.close(); + return; + } + + UnregisterEndpointInput unregisterEpRpcInput = createUnregisterEndpointInput(port, fwCtx); + try { + RpcResult rpcResult = epService.unregisterEndpoint(unregisterEpRpcInput).get(); + if (!rpcResult.isSuccessful()) { + LOG.warn("Illegal state - RPC unregisterEndpoint failed. Input of RPC: {}", unregisterEpRpcInput); + } + } catch (InterruptedException | ExecutionException e) { + LOG.error("addPort - RPC invocation failed.", e); + } finally { + rTx.close(); + } + } + + private static RegisterEndpointInput createRegisterEndpointInput(NeutronPort port, ForwardingCtx fwCtx) { + List epgIds = new ArrayList<>(); + // each EP has to be in EPG ANY, except dhcp and router + if (isDhcpPort(port)) { + epgIds.add(MappingUtils.EPG_DHCP_ID); + } else if (isRouterInterfacePort(port)) { + epgIds.add(MappingUtils.EPG_ROUTER_ID); + } else if (!containsSecRuleWithRemoteSecGroup(port.getSecurityGroups())) { + epgIds.add(MappingUtils.EPG_ANY_ID); + } + + List securityGroups = port.getSecurityGroups(); + if ((securityGroups == null || securityGroups.isEmpty())) { + if (!isDhcpPort(port) && !isRouterInterfacePort(port)) { + LOG.warn( + "Port {} does not contain any security group. The port should belong to 'default' security group at least.", + port.getPortUUID()); + } + } else { + for (NeutronSecurityGroup secGrp : securityGroups) { + epgIds.add(new EndpointGroupId(secGrp.getSecurityGroupUUID())); + } + } + RegisterEndpointInputBuilder inputBuilder = new RegisterEndpointInputBuilder().setL2Context( + fwCtx.getL2BridgeDomain().getId()) + .setMacAddress(new MacAddress(port.getMacAddress())) + .setTenant(new TenantId(Utils.normalizeUuid(port.getTenantID()))) + .setEndpointGroups(epgIds) + .addAugmentation(OfOverlayContextInput.class, + new OfOverlayContextInputBuilder().setPortName(createTapPortName(port)).build()) + .setTimestamp(System.currentTimeMillis()); + List fixedIPs = port.getFixedIPs(); + // TODO Li msunal this getting of just first IP has to be rewrite when OFOverlay renderer + // will support l3-endpoints. Then we will register L2 and L3 endpoints separately. + Neutron_IPs firstIp = getFirstIp(fixedIPs); + if (firstIp != null) { + inputBuilder.setNetworkContainment(new SubnetId(firstIp.getSubnetUUID())); + L3Address l3Address = new L3AddressBuilder().setIpAddress(Utils.createIpAddress(firstIp.getIpAddress())) + .setL3Context(fwCtx.getL3Context().getId()) + .build(); + inputBuilder.setL3Address(ImmutableList.of(l3Address)); + } + if (!Strings.isNullOrEmpty(port.getName())) { + + } + return inputBuilder.build(); + } + + private static boolean containsSecRuleWithRemoteSecGroup(List secGroups) { + if (secGroups == null) { + return false; + } + for (NeutronSecurityGroup secGroup : secGroups) { + boolean containsSecRuleWithRemoteSecGroup = containsSecRuleWithRemoteSecGroup(secGroup); + if (containsSecRuleWithRemoteSecGroup) { + return true; + } + } + return false; + } + + private static boolean containsSecRuleWithRemoteSecGroup(NeutronSecurityGroup secGroup) { + List secRules = secGroup.getSecurityRules(); + if (secRules == null) { + return false; + } + for (NeutronSecurityRule secRule : secRules) { + if (!Strings.isNullOrEmpty(secRule.getSecurityRemoteGroupID())) { + return true; + } + } + return false; + } + + private static Name createTapPortName(NeutronPort port) { + return new Name("tap" + port.getID().substring(0, 11)); + } + + private static Neutron_IPs getFirstIp(List fixedIPs) { + if (fixedIPs == null || fixedIPs.isEmpty()) { + return null; + } + Neutron_IPs neutron_Ip = fixedIPs.get(0); + if (fixedIPs.size() > 1) { + LOG.warn("Neutron mapper does not support multiple IPs on the same port. Only first IP is selected {}", + neutron_Ip); + } + return neutron_Ip; + } + + private static boolean isDhcpPort(NeutronPort port) { + return DEVICE_OWNER_DHCP.equals(port.getDeviceOwner()); + } + + private static boolean isRouterInterfacePort(NeutronPort port) { + return DEVICE_OWNER_ROUTER_IFACE.equals(port.getDeviceOwner()); + } + + private UnregisterEndpointInput createUnregisterEndpointInput(Endpoint ep) { + UnregisterEndpointInputBuilder inputBuilder = new UnregisterEndpointInputBuilder(); + L2 l2Ep = new L2Builder().setL2Context(ep.getL2Context()).setMacAddress(ep.getMacAddress()).build(); + inputBuilder.setL2(ImmutableList.of(l2Ep)); + // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints. + // Endpoint probably will not have l3-addresses anymore, because L2 and L3 endpoints should + // be registered separately. + if (ep.getL3Address() != null && !ep.getL3Address().isEmpty()) { + List l3Eps = new ArrayList<>(); + for (L3Address ip : ep.getL3Address()) { + l3Eps.add(new L3Builder().setL3Context(ip.getL3Context()).setIpAddress(ip.getIpAddress()).build()); + } + inputBuilder.setL3(l3Eps); + } + return inputBuilder.build(); + } + + private UnregisterEndpointInput createUnregisterEndpointInput(NeutronPort port, ForwardingCtx fwCtx) { + UnregisterEndpointInputBuilder inputBuilder = new UnregisterEndpointInputBuilder(); + L2 l2Ep = new L2Builder().setL2Context(fwCtx.getL2BridgeDomain().getId()) + .setMacAddress(new MacAddress(port.getMacAddress())) + .build(); + inputBuilder.setL2(ImmutableList.of(l2Ep)); + // TODO Li msunal this has to be rewrite when OFOverlay renderer will support l3-endpoints. + // Endpoint probably will not have l3-addresses anymore, because L2 and L3 endpoints should + // be registered separately. + if (port.getFixedIPs() != null && !port.getFixedIPs().isEmpty()) { + inputBuilder.setL3(createL3s(port.getFixedIPs(), fwCtx.getL3Context().getId())); + } + return inputBuilder.build(); + } + + private List createL3s(List neutronIps, L3ContextId l3ContextId) { + List l3s = new ArrayList<>(); + for (Neutron_IPs fixedIp : neutronIps) { + String ip = fixedIp.getIpAddress(); + L3 l3 = new L3Builder().setIpAddress(Utils.createIpAddress(ip)).setL3Context(l3ContextId).build(); + l3s.add(l3); + } + return l3s; + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronRouterAware.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronRouterAware.java new file mode 100644 index 000000000..e105f8a2c --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronRouterAware.java @@ -0,0 +1,365 @@ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils.ForwardingCtx; +import org.opendaylight.neutron.spi.INeutronPortCRUD; +import org.opendaylight.neutron.spi.INeutronRouterAware; +import org.opendaylight.neutron.spi.INeutronRouterCRUD; +import org.opendaylight.neutron.spi.INeutronSubnetCRUD; +import org.opendaylight.neutron.spi.NeutronCRUDInterfaces; +import org.opendaylight.neutron.spi.NeutronPort; +import org.opendaylight.neutron.spi.NeutronRouter; +import org.opendaylight.neutron.spi.NeutronRouter_Interface; +import org.opendaylight.neutron.spi.NeutronSecurityRule; +import org.opendaylight.neutron.spi.NeutronSubnet; +import org.opendaylight.neutron.spi.Neutron_IPs; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomainBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3ContextBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubnetBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; + +public class NeutronRouterAware implements INeutronRouterAware { + + private static final Logger LOG = LoggerFactory.getLogger(NeutronRouterAware.class); + private final DataBroker dataProvider; + private final EndpointService epService; + + public NeutronRouterAware(DataBroker dataProvider, EndpointService epService) { + this.dataProvider = checkNotNull(dataProvider); + this.epService = checkNotNull(epService); + } + + @Override + public int canCreateRouter(NeutronRouter router) { + LOG.trace("canCreateRouter - {}", router); + INeutronRouterCRUD routerInterface = NeutronCRUDInterfaces.getINeutronRouterCRUD(this); + if (routerInterface == null) { + LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName()); + return StatusCode.INTERNAL_SERVER_ERROR; + } + + List allRouters = routerInterface.getAllRouters(); + if (allRouters != null && !allRouters.isEmpty()) { + LOG.warn("Illegal state - Neutron mapper does not support multiple routers yet."); + return StatusCode.FORBIDDEN; + } + return StatusCode.OK; + } + + @Override + public void neutronRouterCreated(NeutronRouter router) { + LOG.trace("neutronRouterCreated - {}", router); + // TODO Li msunal external gateway + } + + @Override + public int canUpdateRouter(NeutronRouter delta, NeutronRouter original) { + LOG.trace("canUpdateRouter - delta: {} original: {}", delta, original); + // TODO Li msunal external gateway + return StatusCode.OK; + } + + @Override + public void neutronRouterUpdated(NeutronRouter router) { + LOG.trace("neutronRouterUpdated - {}", router); + // TODO Li msunal external gateway + } + + @Override + public int canDeleteRouter(NeutronRouter router) { + LOG.trace("canDeleteRouter - {}", router); + // nothing to consider + return StatusCode.OK; + } + + @Override + public void neutronRouterDeleted(NeutronRouter router) { + LOG.trace("neutronRouterDeleted - {}", router); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID())); + Optional potentialEpg = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_ROUTER_ID), rwTx); + if (!potentialEpg.isPresent()) { + LOG.warn("Illegal state - Endpoint group {} does not exist.", MappingUtils.EPG_ROUTER_ID.getValue()); + rwTx.cancel(); + return; + } + DataStoreHelper.submitToDs(rwTx); + } + + @Override + public int canAttachInterface(NeutronRouter router, NeutronRouter_Interface routerInterface) { + LOG.trace("canAttachInterface - router: {} interface: {}", router, routerInterface); + try (ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction()) { + L3ContextId l3ContextId = new L3ContextId(router.getID()); + TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID())); + SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID()); + Optional potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.subnetIid(tenantId, subnetId), rTx); + if (!potentialSubnet.isPresent()) { + LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue()); + return StatusCode.NOT_FOUND; + } + Subnet subnet = potentialSubnet.get(); + L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue()); + ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rTx); + if (fwCtx.getL3Context() != null && fwCtx.getL3Context().equals(l3ContextId)) { + // TODO Be msunal + LOG.warn("Illegal state - Neutron mapper does not support multiple router interfaces in the same subnet yet."); + return StatusCode.FORBIDDEN; + } + return StatusCode.OK; + } + } + + @Override + public void neutronRouterInterfaceAttached(NeutronRouter router, NeutronRouter_Interface routerInterface) { + LOG.trace("neutronRouterInterfaceAttached - router: {} interface: {}", router, routerInterface); + INeutronPortCRUD portInterface = NeutronCRUDInterfaces.getINeutronPortCRUD(this); + if (portInterface == null) { + LOG.warn("Illegal state - No provider for {}", INeutronPortCRUD.class.getName()); + return; + } + + INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this); + if (subnetInterface == null) { + LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName()); + return; + } + + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID())); + L3ContextId l3ContextId = new L3ContextId(router.getID()); + InstanceIdentifier l3ContextIid = IidFactory.l3ContextIid(tenantId, l3ContextId); + Optional potentialL3Context = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + l3ContextIid, rwTx); + // add L3 context if missing + if (!potentialL3Context.isPresent()) { + Name l3ContextName = null; + if (router.getName() != null) { + l3ContextName = new Name(router.getName()); + } + L3Context l3Context = new L3ContextBuilder().setId(l3ContextId) + .setName(l3ContextName) + .setDescription(new Description(MappingUtils.NEUTRON_ROUTER__ + router.getID())) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, l3ContextIid, l3Context); + } + + SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID()); + Optional potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.subnetIid(tenantId, subnetId), rwTx); + if (!potentialSubnet.isPresent()) { + LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue()); + rwTx.cancel(); + return; + } + + // Based on Neutron Northbound - Port representing router interface contains exactly on + // fixed IP + NeutronPort routerPort = portInterface.getPort(routerInterface.getPortUUID()); + Subnet subnet = new SubnetBuilder(potentialSubnet.get()).setVirtualRouterIp( + Utils.createIpAddress(routerPort.getFixedIPs().get(0).getIpAddress())).build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetId), subnet); + if (subnet.getParent() == null) { + LOG.warn("Illegal state - subnet {} does not have a parent.", subnetId.getValue()); + rwTx.cancel(); + return; + } + + L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue()); + ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx); + if (fwCtx.getL2BridgeDomain() == null) { + LOG.warn("Illegal state - l2-flood-domain {} does not have a parent.", l2FdId.getValue()); + rwTx.cancel(); + return; + } + + L2BridgeDomain l2BridgeDomain = new L2BridgeDomainBuilder(fwCtx.getL2BridgeDomain()).setParent(l3ContextId) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BridgeDomain.getId()), + l2BridgeDomain); + + // create security rules for router + List routerSecRules = createRouterSecRules(routerPort, null, rwTx); + if (routerSecRules == null) { + rwTx.cancel(); + return; + } + for (NeutronSecurityRule routerSecRule : routerSecRules) { + boolean isRouterSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(routerSecRule, rwTx); + if (!isRouterSecRuleAdded) { + rwTx.cancel(); + return; + } + } + + NeutronSubnet neutronSubnet = subnetInterface.getSubnet(subnetId.getValue()); + List portsInNeutronSubnet = neutronSubnet.getPortsInSubnet(); + for (NeutronPort port : portsInNeutronSubnet) { + boolean isPortAdded = NeutronPortAware.addNeutronPort(port, rwTx, epService); + if (!isPortAdded) { + rwTx.cancel(); + return; + } + } + + DataStoreHelper.submitToDs(rwTx); + } + + public static List createRouterSecRules(NeutronPort port, EndpointGroupId consumerEpgId, + ReadTransaction rTx) { + TenantId tenantId = new TenantId(Utils.normalizeUuid(port.getTenantID())); + Neutron_IPs firstIp = getFirstIp(port.getFixedIPs()); + if (firstIp == null) { + LOG.warn("Illegal state - Router port does not have an IP address."); + return null; + } + SubnetId routerSubnetId = new SubnetId(firstIp.getSubnetUUID()); + Optional potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.subnetIid(tenantId, routerSubnetId), rTx); + if (!potentialSubnet.isPresent()) { + LOG.warn("Illegal state - Subnet {} where is router port does not exist.", routerSubnetId.getValue()); + return null; + } + IpPrefix ipSubnet = potentialSubnet.get().getIpPrefix(); + NeutronSecurityRule routerRuleEgress = createRouterSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId, + true); + NeutronSecurityRule routerRuleIngress = createRouterSecRule(port.getID(), tenantId, ipSubnet, consumerEpgId, + false); + return ImmutableList.of(routerRuleEgress, routerRuleIngress); + } + + private static Neutron_IPs getFirstIp(List fixedIPs) { + if (fixedIPs == null || fixedIPs.isEmpty()) { + return null; + } + Neutron_IPs neutron_Ip = fixedIPs.get(0); + if (fixedIPs.size() > 1) { + LOG.warn("Neutron mapper does not support multiple IPs on the same port. Only first IP is selected {}", + neutron_Ip); + } + return neutron_Ip; + } + + private static NeutronSecurityRule createRouterSecRule(String ruleUuid, TenantId tenantId, IpPrefix ipSubnet, + EndpointGroupId consumerEpgId, boolean isEgress) { + NeutronSecurityRule dhcpSecRule = new NeutronSecurityRule(); + dhcpSecRule.setSecurityRuleGroupID(MappingUtils.EPG_ROUTER_ID.getValue()); + dhcpSecRule.setSecurityRuleTenantID(tenantId.getValue()); + dhcpSecRule.setSecurityRuleRemoteIpPrefix(Utils.getStringIpPrefix(ipSubnet)); + if (isEgress) { + dhcpSecRule.setSecurityRuleUUID(NeutronUtils.EGRESS + "__" + ruleUuid); + dhcpSecRule.setSecurityRuleDirection(NeutronUtils.EGRESS); + } else { + dhcpSecRule.setSecurityRuleUUID(NeutronUtils.INGRESS + "__" + ruleUuid); + dhcpSecRule.setSecurityRuleDirection(NeutronUtils.INGRESS); + } + if (ipSubnet.getIpv4Prefix() != null) { + dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv4); + } else { + dhcpSecRule.setSecurityRuleEthertype(NeutronUtils.IPv6); + } + return dhcpSecRule; + } + + @Override + public int canDetachInterface(NeutronRouter router, NeutronRouter_Interface routerInterface) { + LOG.trace("canDetachInterface - router: {} interface: {}", router, routerInterface); + // nothing to consider + return StatusCode.OK; + } + + @Override + public void neutronRouterInterfaceDetached(NeutronRouter router, NeutronRouter_Interface routerInterface) { + LOG.trace("neutronRouterInterfaceDetached - router: {} interface: {}", router, routerInterface); + INeutronSubnetCRUD subnetInterface = NeutronCRUDInterfaces.getINeutronSubnetCRUD(this); + if (subnetInterface == null) { + LOG.warn("Illegal state - No provider for {}", INeutronSubnetCRUD.class.getName()); + return; + } + + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + TenantId tenantId = new TenantId(Utils.normalizeUuid(router.getTenantID())); + L3ContextId l3ContextId = new L3ContextId(router.getID()); + SubnetId subnetId = new SubnetId(routerInterface.getSubnetUUID()); + InstanceIdentifier l3ContextIid = IidFactory.l3ContextIid(tenantId, l3ContextId); + DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.l3ContextIid(tenantId, l3ContextId), rwTx); + + Optional potentialSubnet = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.subnetIid(tenantId, subnetId), rwTx); + if (!potentialSubnet.isPresent()) { + LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue()); + rwTx.cancel(); + return; + } + + Subnet subnet = new SubnetBuilder(potentialSubnet.get()).setVirtualRouterIp(null).build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetId), subnet); + + L2FloodDomainId l2FdId = new L2FloodDomainId(subnet.getParent().getValue()); + ForwardingCtx fwCtx = MappingUtils.createForwardingContext(tenantId, l2FdId, rwTx); + if (fwCtx.getL2BridgeDomain() == null) { + LOG.warn("Illegal state - l2-flood-domain {} does not have a parent.", l2FdId.getValue()); + rwTx.cancel(); + return; + } + + Optional potentialNetworkMapping = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL, + IidFactory.networkMappingIid(l2FdId), rwTx); + if (!potentialNetworkMapping.isPresent()) { + LOG.warn("Illegal state - network-mapping {} does not exist.", l2FdId.getValue()); + rwTx.cancel(); + return; + } + + L2BridgeDomain l2BridgeDomain = new L2BridgeDomainBuilder(fwCtx.getL2BridgeDomain()).setParent( + potentialNetworkMapping.get().getL3ContextId()).build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.l2BridgeDomainIid(tenantId, l2BridgeDomain.getId()), + l2BridgeDomain); + + NeutronSubnet neutronSubnet = subnetInterface.getSubnet(subnetId.getValue()); + List portsInNeutronSubnet = neutronSubnet.getPortsInSubnet(); + for (NeutronPort port : portsInNeutronSubnet) { + boolean isPortAdded = NeutronPortAware.addNeutronPort(port, rwTx, epService); + if (!isPortAdded) { + rwTx.cancel(); + return; + } + } + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSecurityGroupAware.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSecurityGroupAware.java new file mode 100644 index 000000000..686d1194b --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSecurityGroupAware.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils; +import org.opendaylight.neutron.spi.INeutronSecurityGroupAware; +import org.opendaylight.neutron.spi.NeutronSecurityGroup; +import org.opendaylight.neutron.spi.NeutronSecurityRule; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroupBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ListMultimap; + +public class NeutronSecurityGroupAware implements INeutronSecurityGroupAware { + + private static final Logger LOG = LoggerFactory.getLogger(NeutronSecurityGroupAware.class); + private final DataBroker dataProvider; + + public NeutronSecurityGroupAware(DataBroker dataProvider) { + this.dataProvider = checkNotNull(dataProvider); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSecurityGroupAware#canCreateNeutronSecurityGroup(org.opendaylight.neutron.spi.NeutronSecurityGroup) + */ + @Override + public int canCreateNeutronSecurityGroup(NeutronSecurityGroup securityGroup) { + LOG.trace("canCreateNeutronSecurityGroup - {}", securityGroup); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSecurityGroupAware#neutronSecurityGroupCreated(org.opendaylight.neutron.spi.NeutronSecurityGroup) + */ + @Override + public void neutronSecurityGroupCreated(NeutronSecurityGroup secGroup) { + LOG.trace("neutronSecurityGroupCreated - {}", secGroup); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + boolean isSecGroupCreated = addNeutronSecurityGroup(secGroup, rwTx); + if (isSecGroupCreated) { + DataStoreHelper.submitToDs(rwTx); + } else { + rwTx.cancel(); + } + } + + public static boolean addNeutronSecurityGroup(NeutronSecurityGroup secGroup, ReadWriteTransaction rwTx) { + TenantId tenantId = new TenantId(Utils.normalizeUuid(secGroup.getSecurityGroupTenantID())); + EndpointGroupId providerEpgId = new EndpointGroupId(secGroup.getSecurityGroupUUID()); + EndpointGroupBuilder providerEpgBuilder = new EndpointGroupBuilder().setId(providerEpgId); + providerEpgBuilder.setName(new Name(MappingUtils.NEUTRON_GROUP__ + Strings.nullToEmpty(secGroup.getSecurityGroupName()))); + providerEpgBuilder.setDescription(new Description(MappingUtils.NEUTRON_GROUP__ + + Strings.nullToEmpty(secGroup.getSecurityGroupDescription()))); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.endpointGroupIid(tenantId, providerEpgId), + providerEpgBuilder.build(), true); + List secRules = secGroup.getSecurityRules(); + SortedSecurityGroupRules sortedSecGrpRules = new SortedSecurityGroupRules(secRules); + ListMultimap secRuleByRemoteSecGrpId = sortedSecGrpRules.secRuleByRemoteSecGrpId; + for (EndpointGroupId consumerEpgId : secRuleByRemoteSecGrpId.keySet()) { + addEpgIfMissing(tenantId, consumerEpgId, rwTx); + boolean areSecRulesAdded = addNeutronSecurityRule(secRuleByRemoteSecGrpId.get(consumerEpgId), rwTx); + if (!areSecRulesAdded) { + return false; + } + } + ListMultimap secRuleByRemoteIpPrefix = sortedSecGrpRules.secRuleByRemoteIpPrefix; + for (IpPrefix remoteIpPrefex : secRuleByRemoteIpPrefix.keySet()) { + boolean areSecRulesAdded = addNeutronSecurityRule(secRuleByRemoteIpPrefix.get(remoteIpPrefex), rwTx); + if (!areSecRulesAdded) { + return false; + } + } + boolean areSecRulesAdded = addNeutronSecurityRule(sortedSecGrpRules.secRulesWithoutRemote, rwTx); + if (!areSecRulesAdded) { + return false; + } + return true; + } + + public static void addEpgIfMissing(TenantId tenantId, EndpointGroupId epgId, ReadWriteTransaction rwTx) { + InstanceIdentifier epgIid = IidFactory.endpointGroupIid(tenantId, epgId); + Optional potentialConsumerEpg = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + epgIid, rwTx); + if (!potentialConsumerEpg.isPresent()) { + EndpointGroup epg = new EndpointGroupBuilder().setId(epgId) + .setName(new Name(MappingUtils.NEUTRON_GROUP__)) + .setDescription(new Description(MappingUtils.NEUTRON_GROUP__ + "EPG was created just based on remote group ID from a security rule.")) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, epgIid, epg); + } + } + + private static boolean addNeutronSecurityRule(List secRules, ReadWriteTransaction rwTx) { + for (NeutronSecurityRule secRule : secRules) { + boolean isSecRuleAdded = NeutronSecurityRuleAware.addNeutronSecurityRule(secRule, rwTx); + if (!isSecRuleAdded) { + return false; + } + } + return true; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSecurityGroupAware#canUpdateNeutronSecurityGroup(org.opendaylight.neutron.spi.NeutronSecurityGroup, + * org.opendaylight.neutron.spi.NeutronSecurityGroup) + */ + @Override + public int canUpdateNeutronSecurityGroup(NeutronSecurityGroup delta, NeutronSecurityGroup original) { + LOG.warn("canUpdateNeutronSecurityGroup - Never should be called " + + "- neutron API does not allow UPDATE on neutron security group. \nDelta: {} \nOriginal: {}", delta, + original); + return StatusCode.BAD_REQUEST; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSecurityGroupAware#neutronSecurityGroupUpdated(org.opendaylight.neutron.spi.NeutronSecurityGroup) + */ + @Override + public void neutronSecurityGroupUpdated(NeutronSecurityGroup securityGroup) { + LOG.warn("neutronSecurityGroupUpdated - Never should be called " + + "- neutron API does not allow UPDATE on neutron security group. \nSecurity group: {}", securityGroup); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSecurityGroupAware#canDeleteNeutronSecurityGroup(org.opendaylight.neutron.spi.NeutronSecurityGroup) + */ + @Override + public int canDeleteNeutronSecurityGroup(NeutronSecurityGroup securityGroup) { + LOG.trace("canDeleteNeutronSecurityGroup - {}", securityGroup); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSecurityGroupAware#neutronSecurityGroupDeleted(org.opendaylight.neutron.spi.NeutronSecurityGroup) + */ + @Override + public void neutronSecurityGroupDeleted(NeutronSecurityGroup secGroup) { + LOG.trace("neutronSecurityGroupDeleted - {}", secGroup); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + List secRules = secGroup.getSecurityRules(); + if (secRules != null) { + boolean areSecRulesAdded = deleteNeutronSecurityRules(secRules, rwTx); + if (!areSecRulesAdded) { + rwTx.cancel(); + return; + } + } + TenantId tenantId = new TenantId(Utils.normalizeUuid(secGroup.getSecurityGroupTenantID())); + EndpointGroupId epgId = new EndpointGroupId(secGroup.getSecurityGroupUUID()); + Optional potentialEpg = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.endpointGroupIid(tenantId, epgId), rwTx); + if (!potentialEpg.isPresent()) { + LOG.warn("Illegal state - Endpoint group {} does not exist.", epgId.getValue()); + rwTx.cancel(); + return; + } + + DataStoreHelper.submitToDs(rwTx); + } + + private boolean deleteNeutronSecurityRules(List secRules, ReadWriteTransaction rwTx) { + for (NeutronSecurityRule secRule : secRules) { + boolean isSecRuleDeleted = NeutronSecurityRuleAware.deleteNeutronSecurityRule(secRule, rwTx); + if (!isSecRuleDeleted) { + return false; + } + } + return true; + } + + private static final class SortedSecurityGroupRules { + + private final ListMultimap secRuleByRemoteSecGrpId; + private final ListMultimap secRuleByRemoteIpPrefix; + private final List secRulesWithoutRemote; + + private SortedSecurityGroupRules(List securityRules) { + Preconditions.checkNotNull(securityRules); + ListMultimap tmpSecRuleByRemoteSecGrpId = ArrayListMultimap.create(); + ListMultimap tmpSecRuleByRemoteIpPrefix = ArrayListMultimap.create(); + List tmpSecRulesWithoutRemote = new ArrayList<>(); + for (NeutronSecurityRule securityRule : securityRules) { + String remoteSecGroupId = securityRule.getSecurityRemoteGroupID(); + String remoteIpPrefix = securityRule.getSecurityRuleRemoteIpPrefix(); + boolean isRemoteSecGroupId = remoteSecGroupId != null && !"null".equals(remoteSecGroupId); + boolean isRemoteIpPrefix = remoteIpPrefix != null && !"null".equals(remoteIpPrefix); + if (isRemoteSecGroupId && isRemoteIpPrefix) { + throw new IllegalArgumentException("Either remote group id or ip prefix " + + "must be speciefied in neutron security group rule." + securityRule.toString()); + } + if (isRemoteSecGroupId) { + tmpSecRuleByRemoteSecGrpId.put(new EndpointGroupId(remoteSecGroupId), securityRule); + } else if (isRemoteIpPrefix) { + tmpSecRuleByRemoteIpPrefix.put(Utils.createIpPrefix(remoteIpPrefix), securityRule); + } else { + tmpSecRulesWithoutRemote.add(securityRule); + } + } + secRuleByRemoteSecGrpId = ImmutableListMultimap.copyOf(tmpSecRuleByRemoteSecGrpId); + secRuleByRemoteIpPrefix = ImmutableListMultimap.copyOf(tmpSecRuleByRemoteIpPrefix); + secRulesWithoutRemote = ImmutableList.copyOf(tmpSecRulesWithoutRemote); + } + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSecurityRuleAware.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSecurityRuleAware.java new file mode 100644 index 000000000..bb60a5d40 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSecurityRuleAware.java @@ -0,0 +1,373 @@ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.UUID; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; +import org.opendaylight.neutron.spi.INeutronSecurityRuleAware; +import org.opendaylight.neutron.spi.NeutronSecurityRule; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.endpoint.group.pair.to.contract.mappings.EndpointGroupPairToContractMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.endpoint.group.pair.to.contract.mappings.EndpointGroupPairToContractMappingBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Contract; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.ContractBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup.IntraGroupPolicy; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroupBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelectorBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelectorBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; + +public class NeutronSecurityRuleAware implements INeutronSecurityRuleAware { + + private static final Logger LOG = LoggerFactory.getLogger(NeutronSecurityRuleAware.class); + private final DataBroker dataProvider; + + public NeutronSecurityRuleAware(DataBroker dataProvider) { + this.dataProvider = checkNotNull(dataProvider); + } + + @Override + public int canCreateNeutronSecurityRule(NeutronSecurityRule securityRule) { + LOG.trace("canCreateNeutronSecurityRule - {}", securityRule); + // nothing to consider + return StatusCode.OK; + } + + @Override + public void neutronSecurityRuleCreated(NeutronSecurityRule securityRule) { + LOG.trace("neutronSecurityRuleCreated - {}", securityRule); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + boolean isNeutronSecurityRuleAdded = addNeutronSecurityRule(securityRule, rwTx); + if (isNeutronSecurityRuleAdded) { + DataStoreHelper.submitToDs(rwTx); + } else { + rwTx.cancel(); + } + } + + /** + * ASSUMPTION: Endpoint group with id + * {@link NeutronSecurityRule#getSecurityRuleGroupID()} and + * endpoint group with id {@link NeutronSecurityRule#getSecurityRemoteGroupID()} already exist + * in transaction. + * + * @param secRule neutron security rule from which GBP entities are created + * @param rwTx GBP entities are stored to this transaction. This method NEVER submits or cancel + * the transaction. + * @return {@code true} if operation was successful; {@code false} if an illegal state occurs - + * the transaction may contain just partial result + */ + public static boolean addNeutronSecurityRule(NeutronSecurityRule secRule, ReadWriteTransaction rwTx) { + TransformSecRule transform = new TransformSecRule(secRule); + TenantId tenantId = transform.getTenantId(); + EndpointGroupId providerEpgId = transform.getProviderEpgId(); + EndpointGroupId consumerEpgId = transform.getConsumerEpgId(); + SubjectName subjectName = transform.getSubjectName(); + + Optional potentialContractId = readContractIdFromEpgPairToContractMapping(providerEpgId, + consumerEpgId, rwTx); + ContractId contractId = null; + if (potentialContractId.isPresent()) { + contractId = potentialContractId.get(); + Optional potentialSubject = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.subjectIid(tenantId, contractId, subjectName), rwTx); + if (!potentialSubject.isPresent()) { + // it also means that clause for this subject does not exist + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subjectIid(tenantId, contractId, subjectName), + transform.createSubject()); + rwTx.put(LogicalDatastoreType.CONFIGURATION, + IidFactory.clauseIid(tenantId, contractId, transform.getClauseName()), transform.createClause()); + } + } else { + // check assumption that provider EPG exists + Optional potentialProviderEpg = DataStoreHelper.readFromDs( + LogicalDatastoreType.CONFIGURATION, IidFactory.endpointGroupIid(tenantId, providerEpgId), rwTx); + if (!potentialProviderEpg.isPresent()) { + LOG.warn("Illegal state - Endpoint group {} does not exist.", providerEpgId.getValue()); + return false; + } + + if (providerEpgId.equals(consumerEpgId)) { + EndpointGroup providerConsumerEpg = potentialProviderEpg.get(); + if (providerConsumerEpg.getIntraGroupPolicy() == null + || !providerConsumerEpg.getIntraGroupPolicy().equals(IntraGroupPolicy.RequireContract)) { + EndpointGroup newProviderConsumerEpg = new EndpointGroupBuilder(providerConsumerEpg).setIntraGroupPolicy( + IntraGroupPolicy.RequireContract) + .build(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.endpointGroupIid(tenantId, providerEpgId), + newProviderConsumerEpg); + } + } else { + Optional potentialConsumerEpg = DataStoreHelper.readFromDs( + LogicalDatastoreType.CONFIGURATION, IidFactory.endpointGroupIid(tenantId, consumerEpgId), rwTx); + if (!potentialConsumerEpg.isPresent()) { + if (MappingUtils.EPG_ANY_ID.equals(consumerEpgId)) { + EndpointGroup epgAny = createEpgAny(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.endpointGroupIid(tenantId, MappingUtils.EPG_ANY_ID), + epgAny); + } else { + LOG.warn("Illegal state - Endpoint group {} does not exist.", consumerEpgId.getValue()); + return false; + } + } + } + // creates and stores contract with clause and subject + Subject subject = transform.createSubject(); + Clause clause = transform.createClause(); + Contract contract = createContract(clause, subject); + contractId = contract.getId(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.contractIid(tenantId, contractId), contract); + putEpgPairToContractMapping(providerEpgId, consumerEpgId, contractId, rwTx); + + // adds provider and consumer named selectors + ProviderNamedSelector providerSelector = createProviderNamedSelector(contractId); + rwTx.put(LogicalDatastoreType.CONFIGURATION, + IidFactory.providerNamedSelectorIid(tenantId, providerEpgId, providerSelector.getName()), + providerSelector); + ConsumerNamedSelector consumerSelector = createConsumerNamedSelector(contractId); + rwTx.put(LogicalDatastoreType.CONFIGURATION, + IidFactory.consumerNamedSelectorIid(tenantId, consumerEpgId, consumerSelector.getName()), + consumerSelector); + } + + // create classifier-instance + ClassifierName classifierName = transform.getClassifierName(); + ClassifierInstance classifier = transform.createClassifier(); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.classifierInstanceIid(tenantId, classifierName), + classifier, true); + // create action-instance if it does not exist yet + Optional potentialAction = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.actionInstanceIid(tenantId, MappingUtils.ACTION_ALLOW.getName()), rwTx); + if (!potentialAction.isPresent()) { + rwTx.put(LogicalDatastoreType.CONFIGURATION, + IidFactory.actionInstanceIid(tenantId, MappingUtils.ACTION_ALLOW.getName()), + MappingUtils.ACTION_ALLOW, true); + } + + // create rule + Rule rule = transform.createRule(0); + rwTx.put(LogicalDatastoreType.CONFIGURATION, + IidFactory.ruleIid(tenantId, contractId, subjectName, rule.getName()), rule); + return true; + } + + private static EndpointGroup createEpgAny() { + return new EndpointGroupBuilder().setId(MappingUtils.EPG_ANY_ID) + .setDescription(new Description(MappingUtils.NEUTRON_RULE__ + "epg_any")) + .setIntraGroupPolicy(IntraGroupPolicy.RequireContract) + .build(); + } + + @Override + public int canUpdateNeutronSecurityRule(NeutronSecurityRule delta, NeutronSecurityRule original) { + LOG.warn("canUpdateNeutronSecurityRule - Never should be called " + + "- neutron API does not allow UPDATE on neutron security group rule. \nDelta: {} \nOriginal: {}", + delta, original); + return StatusCode.BAD_REQUEST; + } + + @Override + public void neutronSecurityRuleUpdated(NeutronSecurityRule securityRule) { + LOG.warn("neutronSecurityRuleUpdated - Never should be called " + + "- neutron API does not allow UPDATE on neutron security group rule. \nSecurity group rule: {}", + securityRule); + } + + @Override + public int canDeleteNeutronSecurityRule(NeutronSecurityRule securityRule) { + LOG.trace("canDeleteNeutronSecurityRule - {}", securityRule); + // nothing to consider + return StatusCode.OK; + } + + @Override + public void neutronSecurityRuleDeleted(NeutronSecurityRule secRule) { + LOG.trace("neutronSecurityRuleCreated - {}", secRule); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + boolean isNeutronSecurityRuleDelete = deleteNeutronSecurityRule(secRule, rwTx); + if (isNeutronSecurityRuleDelete) { + DataStoreHelper.submitToDs(rwTx); + } else { + DataStoreHelper.submitToDs(rwTx); + } + } + + /** + * @param secRule neutron security rule from which GBP entities are deleted + * @param rwTx GBP entities are stored to this transaction. This method NEVER submits or cancel + * the transaction. + * @return {@code true} if operation was successful; {@code false} if an illegal state occurs - + * the transaction may contain just partial result + */ + public static boolean deleteNeutronSecurityRule(NeutronSecurityRule secRule, ReadWriteTransaction rwTx) { + TransformSecRule transform = new TransformSecRule(secRule); + TenantId tenantId = transform.getTenantId(); + EndpointGroupId providerEpgId = transform.getProviderEpgId(); + EndpointGroupId consumerEpgId = transform.getConsumerEpgId(); + + Optional potentialContractId = readContractIdFromEpgPairToContractMapping(providerEpgId, + consumerEpgId, rwTx); + if (!potentialContractId.isPresent()) { + LOG.warn("Illegal state - mapping EPG pair (provider EPG {} consumer EPG {}) does not exist.", + providerEpgId.getValue(), consumerEpgId.getValue()); + return false; + } + + ContractId contractId = potentialContractId.get(); + ClassifierName classifierName = transform.getClassifierName(); + InstanceIdentifier classifierIid = IidFactory.classifierInstanceIid(tenantId, + classifierName); + Optional potentialClassifier = DataStoreHelper.removeIfExists( + LogicalDatastoreType.CONFIGURATION, classifierIid, rwTx); + if (!potentialClassifier.isPresent()) { + LOG.warn("Illegal state - classifier-instance {} does not exist. {}", classifierName.getValue(), + classifierIid); + return false; + } + + RuleName ruleName = transform.getRuleName(); + SubjectName subjectName = transform.getSubjectName(); + InstanceIdentifier ruleIid = IidFactory.ruleIid(tenantId, contractId, subjectName, ruleName); + Optional potentionalRule = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, ruleIid, + rwTx); + if (!potentionalRule.isPresent()) { + LOG.warn("Illegal state - rule {} does not exist. {}", ruleName.getValue(), ruleIid); + return false; + } + + InstanceIdentifier subjectIid = IidFactory.subjectIid(tenantId, contractId, subjectName); + Optional potentionalSubject = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + subjectIid, rwTx); + if (!potentionalSubject.isPresent()) { + LOG.warn("Illegal state - subject {} does not exist. {}", subjectName.getValue(), subjectName); + return false; + } + + ClauseName clauseName = transform.getClauseName(); + InstanceIdentifier clauseIid = IidFactory.clauseIid(tenantId, contractId, clauseName); + Optional potentialClause = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, clauseIid, + rwTx); + if (!potentialClause.isPresent()) { + LOG.warn("Illegal state - clause {} does not exist. {}", clauseName.getValue(), clauseIid); + return false; + } + + Subject subject = potentionalSubject.get(); + if (subject.getRule() == null || subject.getRule().isEmpty()) { + rwTx.delete(LogicalDatastoreType.CONFIGURATION, clauseIid); + rwTx.delete(LogicalDatastoreType.CONFIGURATION, subjectIid); + } + + InstanceIdentifier contractIid = IidFactory.contractIid(tenantId, contractId); + Optional potentialContract = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + contractIid, rwTx); + if (!potentialContract.isPresent()) { + LOG.warn("Illegal state - contract {} does not exist. {}", contractId.getValue(), contractIid); + return false; + } + + Contract contract = potentialContract.get(); + if (contract.getSubject() == null || contract.getSubject().isEmpty()) { + // remove contract and named selectors from EPGs + rwTx.delete(LogicalDatastoreType.CONFIGURATION, contractIid); + SelectorName providerSelectorName = createNameOfNamedSelector(contractId); + InstanceIdentifier providerSelectorIid = IidFactory.providerNamedSelectorIid( + tenantId, providerEpgId, providerSelectorName); + Optional potentialProviderSelector = DataStoreHelper.removeIfExists( + LogicalDatastoreType.CONFIGURATION, providerSelectorIid, rwTx); + if (!potentialProviderSelector.isPresent()) { + LOG.warn("Illegal state - provider-name-selector {} does not exist. {}", + providerSelectorName.getValue(), providerSelectorIid); + return false; + } + SelectorName consumerSelectorName = createNameOfNamedSelector(contractId); + InstanceIdentifier consumerSelectorIid = IidFactory.consumerNamedSelectorIid( + tenantId, consumerEpgId, consumerSelectorName); + Optional potentialConsuemrSelector = DataStoreHelper.removeIfExists( + LogicalDatastoreType.CONFIGURATION, consumerSelectorIid, rwTx); + if (!potentialConsuemrSelector.isPresent()) { + LOG.warn("Illegal state - consumer-name-selector {} does not exist. {}", + consumerSelectorName.getValue(), consumerSelectorIid); + return false; + } + } + return true; + } + + public static Optional readContractIdFromEpgPairToContractMapping(EndpointGroupId providerEpgId, + EndpointGroupId consumerEpgId, ReadTransaction rTx) { + Optional potentialMapping = DataStoreHelper.readFromDs( + LogicalDatastoreType.OPERATIONAL, + IidFactory.endpointGroupPairToContractMappingIid(providerEpgId, consumerEpgId), rTx); + if (potentialMapping.isPresent()) { + return Optional.of(potentialMapping.get().getContractId()); + } + return Optional.absent(); + } + + private static void putEpgPairToContractMapping(EndpointGroupId providerEpgId, EndpointGroupId consumerEpgId, + ContractId contractId, WriteTransaction wTx) { + EndpointGroupPairToContractMapping epgPairToContractMapping = new EndpointGroupPairToContractMappingBuilder().setProviderEpgId( + providerEpgId) + .setConsumerEpgId(consumerEpgId) + .setContractId(contractId) + .build(); + wTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.endpointGroupPairToContractMappingIid( + epgPairToContractMapping.getProviderEpgId(), epgPairToContractMapping.getConsumerEpgId()), + epgPairToContractMapping, true); + } + + private static Contract createContract(Clause clause, Subject subject) { + ContractId contractId = new ContractId(UUID.randomUUID().toString()); + return new ContractBuilder().setId(contractId) + .setClause(ImmutableList.of(clause)) + .setSubject(ImmutableList.of(subject)) + .build(); + } + + private static ProviderNamedSelector createProviderNamedSelector(ContractId contractId) { + return new ProviderNamedSelectorBuilder().setName(createNameOfNamedSelector(contractId)) + .setContract(ImmutableList.of(contractId)) + .build(); + } + + private static ConsumerNamedSelector createConsumerNamedSelector(ContractId contractId) { + return new ConsumerNamedSelectorBuilder().setName(createNameOfNamedSelector(contractId)) + .setContract(ImmutableList.of(contractId)) + .build(); + } + + private static SelectorName createNameOfNamedSelector(ContractId contractId) { + return new SelectorName(MappingUtils.NEUTRON_RULE__ + contractId.getValue()); + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSubnetAware.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSubnetAware.java new file mode 100644 index 000000000..f1df5d1ad --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/NeutronSubnetAware.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.DataStoreHelper; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.IidFactory; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils; +import org.opendaylight.neutron.spi.INeutronSubnetAware; +import org.opendaylight.neutron.spi.NeutronSubnet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Name; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubnetBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; + +public class NeutronSubnetAware implements INeutronSubnetAware { + + private final static Logger LOG = LoggerFactory.getLogger(NeutronSubnetAware.class); + private final DataBroker dataProvider; + + public NeutronSubnetAware(DataBroker dataProvider) { + this.dataProvider = checkNotNull(dataProvider); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSubnetAware#canCreateSubnet(org.opendaylight.neutron.spi.NeutronSubnet) + */ + @Override + public int canCreateSubnet(NeutronSubnet subnet) { + LOG.trace("canCreateSubnet - {}", subnet); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSubnetAware#neutronSubnetCreated(org.opendaylight.neutron.spi.NeutronSubnet) + */ + @Override + public void neutronSubnetCreated(NeutronSubnet neutronSubnet) { + LOG.trace("neutronSubnetCreated - {}", neutronSubnet); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + SubnetId subnetId = new SubnetId(Utils.normalizeUuid(neutronSubnet.getID())); + TenantId tenantId = new TenantId(Utils.normalizeUuid(neutronSubnet.getTenantID())); + Subnet subnet = createSubnet(neutronSubnet); + rwTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.subnetIid(tenantId, subnetId), subnet, true); + + DataStoreHelper.submitToDs(rwTx); + } + + private Subnet createSubnet(NeutronSubnet neutronSubnet) { + SubnetBuilder subnetBuilder = new SubnetBuilder(); + subnetBuilder.setId(new SubnetId(neutronSubnet.getID())); + subnetBuilder.setParent(new ContextId(neutronSubnet.getNetworkUUID())); + if (neutronSubnet.getName() != null) { + subnetBuilder.setName(new Name(neutronSubnet.getName())); + } + subnetBuilder.setIpPrefix(Utils.createIpPrefix(neutronSubnet.getCidr())); + return subnetBuilder.build(); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSubnetAware#canUpdateSubnet(org.opendaylight.neutron.spi.NeutronSubnet, + * org.opendaylight.neutron.spi.NeutronSubnet) + */ + @Override + public int canUpdateSubnet(NeutronSubnet delta, NeutronSubnet original) { + LOG.trace("canUpdateSubnet - delta: {} original: {}", delta, original); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSubnetAware#neutronSubnetUpdated(org.opendaylight.neutron.spi.NeutronSubnet) + */ + @Override + public void neutronSubnetUpdated(NeutronSubnet subnet) { + LOG.trace("neutronSubnetUpdated - {}", subnet); + neutronSubnetCreated(subnet); + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSubnetAware#canDeleteSubnet(org.opendaylight.neutron.spi.NeutronSubnet) + */ + @Override + public int canDeleteSubnet(NeutronSubnet subnet) { + LOG.trace("canDeleteSubnet - {}", subnet); + // nothing to consider + return StatusCode.OK; + } + + /** + * @see org.opendaylight.neutron.spi.INeutronSubnetAware#neutronSubnetDeleted(org.opendaylight.neutron.spi.NeutronSubnet) + */ + @Override + public void neutronSubnetDeleted(NeutronSubnet neutronSubnet) { + LOG.trace("neutronSubnetDeleted - {}", neutronSubnet); + ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction(); + SubnetId subnetId = new SubnetId(Utils.normalizeUuid(neutronSubnet.getID())); + TenantId tenantId = new TenantId(Utils.normalizeUuid(neutronSubnet.getTenantID())); + Optional potentialSubnet = DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, + IidFactory.subnetIid(tenantId, subnetId), rwTx); + if (!potentialSubnet.isPresent()) { + LOG.warn("Illegal state - subnet {} does not exist.", subnetId.getValue()); + rwTx.cancel(); + return; + } + + DataStoreHelper.submitToDs(rwTx); + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/StatusCode.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/StatusCode.java new file mode 100644 index 000000000..e0dfb1505 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/StatusCode.java @@ -0,0 +1,17 @@ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +/** + * HTTP status codes + */ +public final class StatusCode { + + public static final int OK = 200; + public static final int BAD_REQUEST = 400; + public static final int FORBIDDEN = 403; + public static final int NOT_FOUND = 404; + public static final int INTERNAL_SERVER_ERROR = 500; + + private StatusCode() { + throw new UnsupportedOperationException("Cannot create an instance."); + } +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/TransformSecRule.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/TransformSecRule.java new file mode 100644 index 000000000..8940bb911 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/mapping/TransformSecRule.java @@ -0,0 +1,263 @@ +package org.opendaylight.groupbasedpolicy.neutron.mapper.mapping; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.MappingUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.NeutronUtils; +import org.opendaylight.groupbasedpolicy.neutron.mapper.util.Utils; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.EtherTypeClassifier; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.IpProtoClassifier; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.L4Classifier; +import org.opendaylight.neutron.spi.NeutronSecurityRule; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRefBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef.ConnectionTracking; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRefBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValueBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.parameter.value.RangeValueBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.ClauseBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.SubjectBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ConsumerMatchersBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.ProviderMatchersBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.EndpointIdentificationConstraintsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.endpoint.identification.constraints.L3EndpointIdentificationConstraintsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.endpoint.identification.constraints.l3.endpoint.identification.constraints.PrefixConstraint; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.consumer.matchers.endpoint.identification.constraints.l3.endpoint.identification.constraints.PrefixConstraintBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.RuleBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstanceBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; + +public class TransformSecRule { + + private static final Logger LOG = LoggerFactory.getLogger(TransformSecRule.class); + private static final List ACTION_REF_ALLOW = ImmutableList.of(new ActionRefBuilder().setName( + MappingUtils.ACTION_ALLOW.getName()) + .setOrder(0) + .build()); + private final NeutronSecurityRule secRule; + private final TenantId tenantId; + private final EndpointGroupId providerEpgId; + private final EndpointGroupId consumerEpgId; + private final SubjectName subjectName; + private final ClauseName clauseName; + private final IpPrefix ipPrefix; + private final int subjectOrder; + private final ClassifierName classifierName; + private final RuleName ruleName; + + /** + * If a {@link NeutronSecurityRule#getSecurityRuleGroupID()} is {@link MappingUtils#EPG_DHCP_ID} + * or {@link MappingUtils#EPG_ROUTER_ID} then the neutron security rule can contain remote ip + * prefix besides remote ip security group. I this case {@link #getConsumerEpgId()} returns remote security group id. + * + * @param secRule + */ + public TransformSecRule(NeutronSecurityRule secRule) { + this.secRule = checkNotNull(secRule); + tenantId = new TenantId(Utils.normalizeUuid(secRule.getSecurityRuleTenantID())); + providerEpgId = new EndpointGroupId(secRule.getSecurityRuleGroupID()); + if (!Strings.isNullOrEmpty(secRule.getSecurityRemoteGroupID())) { + consumerEpgId = new EndpointGroupId(secRule.getSecurityRemoteGroupID()); + if (isEpgIdRouterOrDhcp(providerEpgId) && !Strings.isNullOrEmpty(secRule.getSecurityRuleRemoteIpPrefix())) { + ipPrefix = Utils.createIpPrefix(secRule.getSecurityRuleRemoteIpPrefix()); + } else { + ipPrefix = null; + } + subjectOrder = 0; + } else if (!Strings.isNullOrEmpty(secRule.getSecurityRuleRemoteIpPrefix())) { + consumerEpgId = MappingUtils.EPG_ANY_ID; + ipPrefix = Utils.createIpPrefix(secRule.getSecurityRuleRemoteIpPrefix()); + subjectOrder = 0; + } else { + consumerEpgId = MappingUtils.EPG_ANY_ID; + ipPrefix = null; + subjectOrder = 1; + } + subjectName = createSubjectName(); + clauseName = new ClauseName(subjectName.getValue()); + classifierName = new ClassifierName(MappingUtils.NEUTRON_RULE__ + secRule.getSecurityRuleUUID()); + ruleName = new RuleName(MappingUtils.NEUTRON_RULE__ + "Allow--" + classifierName.getValue()); + } + + private SubjectName createSubjectName() { + if (ipPrefix == null) { + return new SubjectName(MappingUtils.NEUTRON_RULE__ + providerEpgId.getValue() + "__" + + consumerEpgId.getValue()); + } + String prefix = Utils.getStringIpPrefix(ipPrefix).replace('/', '_'); + return new SubjectName(MappingUtils.NEUTRON_RULE__ + providerEpgId.getValue() + "__" + prefix + "__" + + consumerEpgId.getValue()); + } + + public Clause createClause() { + ClauseBuilder clauseBuilder = new ClauseBuilder().setName(clauseName).setSubjectRefs( + ImmutableList.of(subjectName)); + if (ipPrefix != null) { + clauseBuilder.setConsumerMatchers(new ConsumerMatchersBuilder().setEndpointIdentificationConstraints( + new EndpointIdentificationConstraintsBuilder().setL3EndpointIdentificationConstraints( + new L3EndpointIdentificationConstraintsBuilder().setPrefixConstraint( + ImmutableList.of(new PrefixConstraintBuilder().setIpPrefix( + ipPrefix).build())).build()).build()).build()); + if (isEpgIdRouterOrDhcp(providerEpgId)) { + clauseBuilder.setProviderMatchers(new ProviderMatchersBuilder().setEndpointIdentificationConstraints( + new org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.EndpointIdentificationConstraintsBuilder().setL3EndpointIdentificationConstraints( + new org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.endpoint.identification.constraints.L3EndpointIdentificationConstraintsBuilder().setPrefixConstraint( + ImmutableList.of(new org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.clause.provider.matchers.endpoint.identification.constraints.l3.endpoint.identification.constraints.PrefixConstraintBuilder().setIpPrefix( + ipPrefix) + .build())) + .build()) + .build()) + .build()); + } + } + return clauseBuilder.build(); + } + + private static boolean isEpgIdRouterOrDhcp(EndpointGroupId epgId) { + return (MappingUtils.EPG_ROUTER_ID.equals(epgId) || MappingUtils.EPG_DHCP_ID.equals(epgId)); + } + + public ClassifierInstance createClassifier() { + ClassifierInstanceBuilder classifierBuilder = new ClassifierInstanceBuilder().setName(classifierName); + List params = new ArrayList<>(); + Integer portMin = secRule.getSecurityRulePortMin(); + Integer portMax = secRule.getSecurityRulePortMax(); + if (portMin != null && portMax != null) { + classifierBuilder.setClassifierDefinitionId(L4Classifier.DEFINITION.getId()); + if (portMin.equals(portMax)) { + params.add(new ParameterValueBuilder().setName(new ParameterName(L4Classifier.DST_PORT_PARAM)) + .setIntValue(portMin.longValue()) + .build()); + } else { + params.add(new ParameterValueBuilder().setName(new ParameterName(L4Classifier.DST_PORT_RANGE_PARAM)) + .setRangeValue( + new RangeValueBuilder().setMin(portMin.longValue()).setMax(portMax.longValue()).build()) + .build()); + } + } + String protocol = secRule.getSecurityRuleProtocol(); + if (!Strings.isNullOrEmpty(protocol)) { + if (classifierBuilder.getClassifierDefinitionId() == null) { + classifierBuilder.setClassifierDefinitionId(IpProtoClassifier.DEFINITION.getId()); + } + if (NeutronUtils.TCP.equals(protocol)) { + params.add(new ParameterValueBuilder().setName(new ParameterName(IpProtoClassifier.PROTO_PARAM)) + .setIntValue(IpProtoClassifier.TCP_VALUE) + .build()); + } else if (NeutronUtils.UDP.equals(protocol)) { + params.add(new ParameterValueBuilder().setName(new ParameterName(IpProtoClassifier.PROTO_PARAM)) + .setIntValue(IpProtoClassifier.UDP_VALUE) + .build()); + } else if (NeutronUtils.ICMP.equals(protocol)) { + params.add(new ParameterValueBuilder().setName(new ParameterName(IpProtoClassifier.PROTO_PARAM)) + .setIntValue(1L) + .build()); + } else if (NeutronUtils.NULL.equals(protocol)) { + LOG.debug("Protocol is not specified in security group rule {}", secRule.getSecurityRuleUUID()); + } else { + throw new IllegalArgumentException("Protocol " + protocol + " is not supported."); + } + } + String ethertype = secRule.getSecurityRuleEthertype(); + if (!Strings.isNullOrEmpty(ethertype)) { + if (classifierBuilder.getClassifierDefinitionId() == null) { + classifierBuilder.setClassifierDefinitionId(EtherTypeClassifier.DEFINITION.getId()); + } + if (NeutronUtils.IPv4.equals(ethertype)) { + params.add(new ParameterValueBuilder().setName(new ParameterName(EtherTypeClassifier.ETHERTYPE_PARAM)) + .setIntValue(EtherTypeClassifier.IPv4_VALUE) + .build()); + } else if (NeutronUtils.IPv6.equals(ethertype)) { + params.add(new ParameterValueBuilder().setName(new ParameterName(EtherTypeClassifier.ETHERTYPE_PARAM)) + .setIntValue(EtherTypeClassifier.IPv6_VALUE) + .build()); + } else { + throw new IllegalArgumentException("Ethertype " + ethertype + " is not supported."); + } + } + return classifierBuilder.setParameterValue(params).build(); + } + + public Rule createRule(int order) { + return new RuleBuilder().setName(ruleName) + .setOrder(order) + .setActionRef(ACTION_REF_ALLOW) + .setClassifierRef(ImmutableList.of(createClassifierRef())) + .build(); + } + + public Subject createSubject() { + return new SubjectBuilder().setName(subjectName).setOrder(subjectOrder).build(); + } + + private ClassifierRef createClassifierRef() { + ClassifierRefBuilder classifierRefBuilder = new ClassifierRefBuilder().setName(classifierName) + .setConnectionTracking(ConnectionTracking.Reflexive) + .setInstanceName(classifierName); + String direction = secRule.getSecurityRuleDirection(); + if (NeutronUtils.INGRESS.equals(direction)) { + classifierRefBuilder.setDirection(Direction.In); + } else if (NeutronUtils.EGRESS.equals(direction)) { + classifierRefBuilder.setDirection(Direction.Out); + } else { + throw new IllegalArgumentException("Direction " + direction + " from security group rule " + + secRule.getSecurityRuleUUID() + " is not supported. Direction can be only 'ingress' or 'egress'."); + } + return classifierRefBuilder.build(); + } + + public TenantId getTenantId() { + return tenantId; + } + + public EndpointGroupId getProviderEpgId() { + return providerEpgId; + } + + public EndpointGroupId getConsumerEpgId() { + return consumerEpgId; + } + + public SubjectName getSubjectName() { + return subjectName; + } + + public ClauseName getClauseName() { + return clauseName; + } + + public IpPrefix getIpPrefix() { + return ipPrefix; + } + + public ClassifierName getClassifierName() { + return classifierName; + } + + public RuleName getRuleName() { + return ruleName; + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/DataStoreHelper.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/DataStoreHelper.java new file mode 100644 index 000000000..79573edce --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/DataStoreHelper.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.groupbasedpolicy.neutron.mapper.util; + +import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; + +/** + * @author Martin Sunal + */ +public class DataStoreHelper { + + private static final Logger LOG = LoggerFactory.getLogger(DataStoreHelper.class); + + /** + * Reads data from datastore as synchrone call. + * @return {@link Optional#isPresent()} is {@code true} if reading was successful and data exists in datastore; {@link Optional#isPresent()} is {@code false} otherwise + */ + public static Optional readFromDs(LogicalDatastoreType store, InstanceIdentifier path, ReadTransaction rTx) { + CheckedFuture, ReadFailedException> resultFuture = rTx.read(store, path); + try { + return resultFuture.checkedGet(); + } catch (ReadFailedException e) { + LOG.warn("Read failed from DS.", e); + return Optional.absent(); + } + } + + /** + * Calls {@link WriteTransaction#submit()} on write transaction. + * @param wTx write transaction + * @return {@code true} if transaction commit was successful; {@code false} otherwise + */ + public static boolean submitToDs(WriteTransaction wTx) { + CheckedFuture submitFuture = wTx.submit(); + try { + submitFuture.checkedGet(); + return true; + } catch (TransactionCommitFailedException e) { + LOG.warn("Transaction commit failed to DS.", e); + return false; + } + } + + /** + * If an element on the path exists in datastore the element is removed and returned as a result. + * {@link Optional#isPresent()} is {@code false} in case that element on path does not exist. + * @return removed element in {@link Optional#get()}; otherwise {@link Optional#absent()} + */ + public static Optional removeIfExists(LogicalDatastoreType store, InstanceIdentifier path, + ReadWriteTransaction rwTx) { + Optional potentialResult = readFromDs(store, path, rwTx); + if (potentialResult.isPresent()) { + rwTx.delete(store, path); + } + return potentialResult; + } + +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/IidFactory.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/IidFactory.java new file mode 100644 index 000000000..ecb69cebc --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/IidFactory.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.groupbasedpolicy.neutron.mapper.util; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClauseName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContractId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.RuleName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SelectorName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubjectName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.Mappings; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.EndpointGroupPairToContractMappings; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.NetworkMappings; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.endpoint.group.pair.to.contract.mappings.EndpointGroupPairToContractMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.endpoint.group.pair.to.contract.mappings.EndpointGroupPairToContractMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMapping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.mapper.rev150223.mappings.network.mappings.NetworkMappingKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.Tenants; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRefKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.TenantKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Contract; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.ContractKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroupKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomainKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomainKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3ContextKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubjectFeatureInstances; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.SubnetKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Clause; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.ClauseKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.Subject; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.SubjectKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.RuleKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ConsumerNamedSelectorKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.endpoint.group.ProviderNamedSelectorKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstanceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstanceKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class IidFactory { + + private IidFactory() { + throw new UnsupportedOperationException(); + } + + public static InstanceIdentifier tenantIid(TenantId id) { + return InstanceIdentifier.builder(Tenants.class).child(Tenant.class, new TenantKey(id)).build(); + } + + public static InstanceIdentifier endpointGroupIid(TenantId tenantId, EndpointGroupId epgId) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(EndpointGroup.class, new EndpointGroupKey(epgId)) + .build(); + } + + public static InstanceIdentifier contractIid(TenantId tenantId, ContractId contractId) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(Contract.class, new ContractKey(contractId)) + .build(); + } + + public static InstanceIdentifier subjectIid(TenantId tenantId, ContractId contractId, + SubjectName subjectName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(Contract.class, new ContractKey(contractId)) + .child(Subject.class, new SubjectKey(subjectName)) + .build(); + } + + public static InstanceIdentifier providerNamedSelectorIid(TenantId tenantId, + EndpointGroupId epgId, SelectorName providerSelectorName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(EndpointGroup.class, new EndpointGroupKey(epgId)) + .child(ProviderNamedSelector.class, new ProviderNamedSelectorKey(providerSelectorName)) + .build(); + } + + public static InstanceIdentifier consumerNamedSelectorIid(TenantId tenantId, + EndpointGroupId epgId, SelectorName consumerSelectorName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(EndpointGroup.class, new EndpointGroupKey(epgId)) + .child(ConsumerNamedSelector.class, new ConsumerNamedSelectorKey(consumerSelectorName)) + .build(); + } + + public static InstanceIdentifier clauseIid(TenantId tenantId, ContractId contractId, ClauseName clauseName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(Contract.class, new ContractKey(contractId)) + .child(Clause.class, new ClauseKey(clauseName)) + .build(); + } + + public static InstanceIdentifier ruleIid(TenantId tenantId, ContractId contractId, SubjectName subjectName, + RuleName ruleName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(Contract.class, new ContractKey(contractId)) + .child(Subject.class, new SubjectKey(subjectName)) + .child(Rule.class, new RuleKey(ruleName)) + .build(); + } + + public static InstanceIdentifier actionInstanceIid(TenantId tenantId, ActionName actionName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(SubjectFeatureInstances.class) + .child(ActionInstance.class, new ActionInstanceKey(actionName)) + .build(); + } + + public static InstanceIdentifier classifierInstanceIid(TenantId tenantId, + ClassifierName classifierName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(SubjectFeatureInstances.class) + .child(ClassifierInstance.class, new ClassifierInstanceKey(classifierName)) + .build(); + } + + public static InstanceIdentifier classifierRefIid(TenantId tenantId, ContractId contractId, + SubjectName subjectName, RuleName ruleName, ClassifierName classifierRefName) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(Contract.class, new ContractKey(contractId)) + .child(Subject.class, new SubjectKey(subjectName)) + .child(Rule.class, new RuleKey(ruleName)) + .child(ClassifierRef.class, new ClassifierRefKey(classifierRefName)) + .build(); + } + + public static InstanceIdentifier networkMappingIid(UniqueId networkId) { + return InstanceIdentifier.builder(Mappings.class) + .child(NetworkMappings.class) + .child(NetworkMapping.class, new NetworkMappingKey(networkId)) + .build(); + } + + public static InstanceIdentifier endpointGroupPairToContractMappingIid( + EndpointGroupId providerEpg, EndpointGroupId consumerEpg) { + return InstanceIdentifier.builder(Mappings.class) + .child(EndpointGroupPairToContractMappings.class) + .child(EndpointGroupPairToContractMapping.class, + new EndpointGroupPairToContractMappingKey(consumerEpg, providerEpg)) + .build(); + } + + public static InstanceIdentifier l2FloodDomainIid(TenantId tenantId, L2FloodDomainId l2FloodDomainId) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(L2FloodDomain.class, new L2FloodDomainKey(l2FloodDomainId)) + .build(); + } + + public static InstanceIdentifier l2BridgeDomainIid(TenantId tenantId, + L2BridgeDomainId l2BridgeDomainId) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(L2BridgeDomain.class, new L2BridgeDomainKey(l2BridgeDomainId)) + .build(); + } + + public static InstanceIdentifier l3ContextIid(TenantId tenantId, L3ContextId l3ContextId) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(L3Context.class, new L3ContextKey(l3ContextId)) + .build(); + } + + public static InstanceIdentifier endpointIid(L2BridgeDomainId l2Context, MacAddress macAddress) { + return InstanceIdentifier.builder(Endpoints.class) + .child(Endpoint.class, new EndpointKey(l2Context, macAddress)) + .build(); + } + + public static InstanceIdentifier endpointL3Iid(L3ContextId l3Context, IpAddress ipAddress) { + return InstanceIdentifier.builder(Endpoints.class) + .child(EndpointL3.class, new EndpointL3Key(ipAddress, l3Context)) + .build(); + } + + public static InstanceIdentifier subnetIid(TenantId tenantId, SubnetId subnetId) { + return InstanceIdentifier.builder(Tenants.class) + .child(Tenant.class, new TenantKey(tenantId)) + .child(Subnet.class, new SubnetKey(subnetId)) + .build(); + } +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/MappingUtils.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/MappingUtils.java new file mode 100644 index 000000000..0f26982a2 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/MappingUtils.java @@ -0,0 +1,90 @@ +package org.opendaylight.groupbasedpolicy.neutron.mapper.util; + +import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.AllowAction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ActionName; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2BridgeDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L2FloodDomainId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance; +import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstanceBuilder; + +import com.google.common.base.Optional; + +public final class MappingUtils { + + public static final String NEUTRON_RULE__ = "neutron_rule__"; + public static final String NEUTRON_NETWORK__ = "neutron_network__"; + public static final String NEUTRON_ROUTER__ = "neutron_router__"; + public static final String NEUTRON_GROUP__ = "neutron_group__"; + public static final ActionInstance ACTION_ALLOW = new ActionInstanceBuilder().setName( + new ActionName(NEUTRON_RULE__ + "allow")) + .setActionDefinitionId(AllowAction.DEFINITION.getId()) + .build(); + public static final EndpointGroupId EPG_ANY_ID = new EndpointGroupId("aaaec0ce-dd5a-11e4-b9d6-1681e6b88ec1"); + public static final EndpointGroupId EPG_DHCP_ID = new EndpointGroupId("ddd6cfe6-dfe5-11e4-8a00-1681e6b88ec1"); + public static final EndpointGroupId EPG_ROUTER_ID = new EndpointGroupId("1118172e-cd84-4933-a35f-749f9a651de9"); + + private MappingUtils() { + throw new UnsupportedOperationException("Cannot create an instance."); + } + + public static ForwardingCtx createForwardingContext(TenantId tenantId, L2FloodDomainId l2FdId, ReadTransaction rTx) { + Optional potentialL2Fd = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.l2FloodDomainIid(tenantId, l2FdId), rTx); + if (!potentialL2Fd.isPresent()) { + return new ForwardingCtx(null, null, null); + } + L2BridgeDomainId l2BdId = potentialL2Fd.get().getParent(); + if (l2BdId == null) { + return new ForwardingCtx(potentialL2Fd.get(), null, null); + } + Optional potentialL2Bd = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.l2BridgeDomainIid(tenantId, l2BdId), rTx); + if (!potentialL2Bd.isPresent()) { + return new ForwardingCtx(potentialL2Fd.get(), null, null); + } + L3ContextId l3ContextId = potentialL2Bd.get().getParent(); + if (l3ContextId == null) { + return new ForwardingCtx(potentialL2Fd.get(), potentialL2Bd.get(), null); + } + Optional potentialL3Context = DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, + IidFactory.l3ContextIid(tenantId, l3ContextId), rTx); + if (!potentialL3Context.isPresent()) { + return new ForwardingCtx(potentialL2Fd.get(), potentialL2Bd.get(), null); + } + return new ForwardingCtx(potentialL2Fd.get(), potentialL2Bd.get(), potentialL3Context.get()); + } + + public static final class ForwardingCtx { + + private final L2FloodDomain l2FloodDomain; + private final L2BridgeDomain l2BridgeDomain; + private final L3Context l3Context; + + private ForwardingCtx(L2FloodDomain l2Fd, L2BridgeDomain l2Bd, L3Context l3Context) { + this.l2FloodDomain = l2Fd; + this.l2BridgeDomain = l2Bd; + this.l3Context = l3Context; + } + + public L2FloodDomain getL2FloodDomain() { + return l2FloodDomain; + } + + public L2BridgeDomain getL2BridgeDomain() { + return l2BridgeDomain; + } + + public L3Context getL3Context() { + return l3Context; + } + + } +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/NeutronUtils.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/NeutronUtils.java new file mode 100644 index 000000000..a55a481ff --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/NeutronUtils.java @@ -0,0 +1,17 @@ +package org.opendaylight.groupbasedpolicy.neutron.mapper.util; + +public final class NeutronUtils { + + public static final String EGRESS = "egress"; + public static final String INGRESS = "ingress"; + public static final String IPv6 = "IPv6"; + public static final String IPv4 = "IPv4"; + public static final String NULL = "null"; + public static final String UDP = "udp"; + public static final String TCP = "tcp"; + public static final String ICMP = "icmp"; + + private NeutronUtils() { + throw new UnsupportedOperationException("Cannot create an instance."); + } +} diff --git a/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/Utils.java b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/Utils.java new file mode 100644 index 000000000..8f30ebbf2 --- /dev/null +++ b/neutron-mapper/src/main/java/org/opendaylight/groupbasedpolicy/neutron/mapper/util/Utils.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.groupbasedpolicy.neutron.mapper.util; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.apache.commons.net.util.SubnetUtils; +import org.apache.commons.net.util.SubnetUtils.SubnetInfo; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix; + +import com.google.common.base.Strings; +import com.google.common.net.InetAddresses; + +/** + * @author Martin Sunal + */ +public class Utils { + + private Utils() {} + + /** + * This implementation does not use nameservice lookups (e.g. no DNS). + * + * @param cidr - format must be valid for regex in {@link Ipv4Prefix} or {@link Ipv6Prefix} + * @return the {@link IpPrefix} having the given cidr string representation + * @throws IllegalArgumentException - if the argument is not a valid CIDR string + */ + public static IpPrefix createIpPrefix(String cidr) { + checkArgument(!Strings.isNullOrEmpty(cidr), "Cannot be null or empty."); + String[] ipAndPrefix = cidr.split("/"); + checkArgument(ipAndPrefix.length == 2, "Bad format."); + InetAddress ip = InetAddresses.forString(ipAndPrefix[0]); + if (ip instanceof Inet4Address) { + return new IpPrefix(new Ipv4Prefix(cidr)); + } + return new IpPrefix(new Ipv6Prefix(cidr)); + } + + /** + * This implementation does not use nameservice lookups (e.g. no DNS). + * + * @param ipAddress - format must be valid for regex in {@link Ipv4Address} or + * {@link Ipv6Address} + * @return the {@link IpAddress} having the given ipAddress string representation + * @throws IllegalArgumentException - if the argument is not a valid IP address string + */ + public static IpAddress createIpAddress(String ipAddress) { + checkArgument(!Strings.isNullOrEmpty(ipAddress), "Cannot be null or empty."); + InetAddress ip = InetAddresses.forString(ipAddress); + if (ip instanceof Inet4Address) { + return new IpAddress(new Ipv4Address(ipAddress)); + } + return new IpAddress(new Ipv6Address(ipAddress)); + } + + public static String getStringIpPrefix(IpPrefix ipPrefix) { + if (ipPrefix.getIpv4Prefix() != null) { + return ipPrefix.getIpv4Prefix().getValue(); + } + return ipPrefix.getIpv6Prefix().getValue(); + } + + public static String getStringIpAddress(IpAddress ipAddress) { + if (ipAddress.getIpv4Address() != null) { + return ipAddress.getIpv4Address().getValue(); + } + return ipAddress.getIpv6Address().getValue(); + } + + public static boolean isHostInIpPrefix(IpAddress host, IpPrefix ipPrefix) { + String ipAddress = ""; + int ipVersion = 0; + if (host.getIpv4Address() != null) { + ipAddress = host.getIpv4Address().getValue(); + ipVersion = 4; + } else { + ipAddress = host.getIpv6Address().getValue(); + ipVersion = 6; + } + String cidr = getStringIpPrefix(ipPrefix); + + if (ipVersion == 4) { + try { + SubnetUtils util = new SubnetUtils(cidr); + SubnetInfo info = util.getInfo(); + return info.isInRange(ipAddress); + } catch (IllegalArgumentException e) { + return false; + } + } + + if (ipVersion == 6) { + String[] parts = cidr.split("/"); + try { + int length = Integer.parseInt(parts[1]); + byte[] cidrBytes = ((Inet6Address) InetAddress.getByName(parts[0])).getAddress(); + byte[] ipBytes = ((Inet6Address) InetAddress.getByName(ipAddress)).getAddress(); + int i; + for (i = 0; i < length; i++) { // offset is to ensure proper comparison + if ((((cidrBytes[i / 8]) & 0x000000FF) & (1 << (7 - (i % 8)))) != (((ipBytes[i / 8]) & 0x000000FF) & (1 << (7 - (i % 8))))) { + return false; + } + } + return true; + } catch (UnknownHostException e) { + return false; + } + } + return false; + } + + public static String normalizeUuid(String string) { + return string.replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", + "$1-$2-$3-$4-$5"); + } + +} diff --git a/neutron-mapper/src/main/yang/mapper.yang b/neutron-mapper/src/main/yang/mapper.yang new file mode 100644 index 000000000..2918bc5e2 --- /dev/null +++ b/neutron-mapper/src/main/yang/mapper.yang @@ -0,0 +1,66 @@ +module neutron-mapper { + yang-version 1; + + namespace "urn:opendaylight:groupbasedpolicy:neutron-mapper"; + prefix "gbp-neutron-mapper"; + + import gbp-common {prefix gbp-common;} + import endpoint {prefix gbp-endpoint;} + import ietf-inet-types {prefix inet;} + + description + "This module defines the mapping model between Neutron IDs and GBP IDs."; + + revision "2015-02-23" { + description + "Initial revision."; + } + + grouping tenant-id-fields { + leaf tenant-id { + description "A unique ID for the tenant"; + mandatory true; + type gbp-common:tenant-id; + } + } + + grouping endpoint-group-pair-fields { + leaf provider-epg-id { + type gbp-common:endpoint-group-id; + } + leaf consumer-epg-id { + type gbp-common:endpoint-group-id; + } + leaf contract-id { + type gbp-common:contract-id; + } + } + + container mappings { + config false; + container network-mappings { + list network-mapping { + key network-id; + leaf network-id { + description "A unique ID for the neutron network == gbp l2-flood-domain"; + type gbp-common:unique-id; + } + leaf l2-bridge-domain-id { + description "A unique ID of l2-bridge-domain generated for neutron network"; + type gbp-common:l2-bridge-domain-id; + } + leaf l3-context-id { + description "A unique ID of l3-context generated for neutron network"; + type gbp-common:l3-context-id; + } + } + } + container endpoint-group-pair-to-contract-mappings { + list endpoint-group-pair-to-contract-mapping { + key "provider-epg-id consumer-epg-id"; + uses endpoint-group-pair-fields; + } + } + } + +} \ No newline at end of file diff --git a/neutron-mapper/src/main/yang/neutron-mapper-impl.yang b/neutron-mapper/src/main/yang/neutron-mapper-impl.yang new file mode 100644 index 000000000..2bc1066a8 --- /dev/null +++ b/neutron-mapper/src/main/yang/neutron-mapper-impl.yang @@ -0,0 +1,52 @@ +module neutron-mapper-impl { + + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:config:neutron-mapper:impl"; + prefix "neutron-mapper-impl"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; } + + description + "This module contains the base YANG definitions for + neutron-mapper implementation."; + + revision "2015-02-19" { + description + "Initial revision."; + } + + // This is the definition of the service implementation as a module identity. + identity neutron-mapper-impl { + base config:module-type; + + // Specifies the prefix for generated java classes. + config:java-name-prefix NeutronMapper; + } + + // Augments the 'configuration' choice node under modules/module. + augment "/config:modules/config:module/config:configuration" { + case neutron-mapper-impl { + when "/config:modules/config:module/config:type = 'neutron-mapper-impl'"; + + container data-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity mdsal:binding-async-data-broker; + } + } + } + + container rpc-registry { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity mdsal:binding-rpc-registry; + } + } + } + + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index fbb96a020..2b17b6327 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,8 @@ groupbasedpolicy-ofoverlay-config groupbasedpolicy-opflex-config groupbasedpolicy-openstackendpoint-config + neutron-mapper + neutron-mapper-config distribution-karaf features @@ -48,4 +50,3 @@ -