diff options
author | Ed Warnicke <eaw@cisco.com> | 2016-01-10 06:15:18 -0800 |
---|---|---|
committer | Robert Varga <nite@hq.sk> | 2016-01-30 00:01:07 +0100 |
commit | 788f314b98ba2b2189e7be6d49edd74c5b9a6322 (patch) | |
tree | ab4b5476d8dba224e6596106d289c625ffbbc323 /v3po/impl |
Initial honeycomb code commit.
This commit drops the basic structure with disabled integration tests.
The tests will be enabled in a follow-up patch, which sorts out the
current .so loading problems.
Change-Id: If70f2f13b2cf49af82996f884218ac05d335c2ed
Signed-off-by: Ed Warnicke <eaw@cisco.com>
Signed-off-by: Robert Varga <nite@hq.sk>
Diffstat (limited to 'v3po/impl')
21 files changed, 2521 insertions, 0 deletions
diff --git a/v3po/impl/pom.xml b/v3po/impl/pom.xml new file mode 100644 index 000000000..9315c15cc --- /dev/null +++ b/v3po/impl/pom.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- vi: set et smarttab sw=4 tabstop=4: --> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>io.fd.honeycomb.common</groupId> + <artifactId>impl-parent</artifactId> + <version>1.0.0-SNAPSHOT</version> + <relativePath>../../common/impl-parent</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>io.fd.honeycomb.v3po</groupId> + <artifactId>v3po-impl</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>v3po-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>io.fd.vpp</groupId> + <artifactId>vppjapi</artifactId> + <version>1.0.0-SNAPSHOT</version> + </dependency> + + <!-- Testing Dependencies --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/v3po/impl/src/main/config/default-config.xml b/v3po/impl/src/main/config/default-config.xml new file mode 100644 index 000000000..08a090ea2 --- /dev/null +++ b/v3po/impl/src/main/config/default-config.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- vi: set et smarttab sw=4 tabstop=4: --> +<!-- + Copyright (c) 2015 Cisco and/or its affiliates. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<snapshot> + <required-capabilities> + <capability>urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&revision=2014-12-10</capability> + <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28</capability> + </required-capabilities> + <configuration> + + <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config"> + <module> + <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:v3po</type> + <name>v3po-default</name> + <broker> + <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type> + <name>binding-osgi-broker</name> + </broker> + </module> + </modules> + </data> + </configuration> +</snapshot> diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataRegistry.java new file mode 100644 index 000000000..5888dc998 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataRegistry.java @@ -0,0 +1,24 @@ +/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface DataRegistry {
+ <T extends DataObject> void registerModule(InstanceIdentifier<T> path, DataResolver<T> resolver);
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataResolver.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataResolver.java new file mode 100644 index 000000000..5ada80f86 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataResolver.java @@ -0,0 +1,26 @@ +/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface DataResolver<T extends DataObject> {
+ public void resolve(InstanceIdentifier<T> path, WriteTransaction tx);
+}
+
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataResolverInterfaceState.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataResolverInterfaceState.java new file mode 100644 index 000000000..fd2d48c01 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/DataResolverInterfaceState.java @@ -0,0 +1,36 @@ +/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package io.fd.honeycomb.v3po.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+// Return Interface data from interfaces.state based on InstanceIdentifier
+public class DataResolverInterfaceState implements DataResolver<Interface> {
+
+ @Override
+ public void resolve(InstanceIdentifier<Interface> path,
+ WriteTransaction tx) {
+ InterfaceKey key = path.firstKeyOf(Interface.class);
+ String interfaceName = key.getName();
+
+ return;
+ }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/InterfaceStateIpv4Builder.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/InterfaceStateIpv4Builder.java new file mode 100644 index 000000000..5e2141d6f --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/InterfaceStateIpv4Builder.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.IpAddressOrigin; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.NeighborOrigin; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv4Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.AddressBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.Neighbor; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.NeighborBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.address.subnet.PrefixLength; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv4.address.subnet.PrefixLengthBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; + +public class InterfaceStateIpv4Builder { + private List<Address> addrs = new ArrayList<Address>(); + private List<Neighbor> neighbors = new ArrayList<Neighbor>(); + private Ipv4Builder ipv4Builder = new Ipv4Builder(); + + /** + * TODO-ADD-JAVADOC. + */ + public void addAddress(String ipv4Addr, short prefixLength, IpAddressOrigin origin) { + // address + AddressBuilder addrBuilder = new AddressBuilder(); + + // IpAddressOrigin.Static + addrBuilder.setOrigin(origin); // FIXME: how to find origin? + + PrefixLength prefixLen = new PrefixLengthBuilder().setPrefixLength(prefixLength).build(); + addrBuilder.setSubnet(prefixLen); + + addrBuilder.setIp(new Ipv4AddressNoZone(ipv4Addr)); + + addrs.add(addrBuilder.build()); + } + + /** + * TODO-ADD-JAVADOC. + */ + public void addNeighbor(String ipv4Addr, String physAddr, NeighborOrigin origin) { + // address neighbor + NeighborBuilder nbrBuilder = new NeighborBuilder(); + nbrBuilder.setIp(new Ipv4AddressNoZone(ipv4Addr)); + nbrBuilder.setLinkLayerAddress(new PhysAddress(physAddr)); // TODO ("00:00:00:00:00:00") + nbrBuilder.setOrigin(origin); + + neighbors.add(nbrBuilder.build()); + } + + /** + * TODO-ADD-JAVADOC. + */ + public void setForwarding(boolean fwd) { + ipv4Builder.setForwarding(fwd); + } + + /** + * TODO-ADD-JAVADOC. + */ + public void setMtu(int mtu) { + ipv4Builder.setMtu(mtu); + } + + /** + * TODO-ADD-JAVADOC. + */ + public Ipv4 build() { + ipv4Builder.setAddress(addrs); + ipv4Builder.setNeighbor(neighbors); + return ipv4Builder.build(); + } +} + diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/InterfaceStateIpv6Builder.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/InterfaceStateIpv6Builder.java new file mode 100644 index 000000000..42862a944 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/InterfaceStateIpv6Builder.java @@ -0,0 +1,87 @@ +/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.IpAddressOrigin;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.NeighborOrigin;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv6;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.Ipv6Builder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv6.Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv6.AddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv6.Neighbor;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces.state._interface.ipv6.NeighborBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+
+public class InterfaceStateIpv6Builder {
+ private List<Address> addrs = new ArrayList<Address>();
+ private List<Neighbor> neighbors = new ArrayList<Neighbor>();
+ private Ipv6Builder ipv6Builder = new Ipv6Builder();
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void addAddress(String ipv6Addr, short prefixLength, IpAddressOrigin origin) {
+ // address
+ AddressBuilder addrBuilder = new AddressBuilder();
+
+ // IpAddressOrigin.Static
+ addrBuilder.setOrigin(origin); // FIXME: how to find origin?
+ addrBuilder.setPrefixLength(prefixLength);
+ addrBuilder.setIp(new Ipv6AddressNoZone(ipv6Addr));
+
+ addrs.add(addrBuilder.build());
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void addNeighbor(String ipv6Addr, String physAddr, NeighborOrigin origin) {
+ // address neighbor
+ NeighborBuilder nbrBuilder = new NeighborBuilder();
+ nbrBuilder.setIp(new Ipv6AddressNoZone(ipv6Addr));
+ nbrBuilder.setLinkLayerAddress(new PhysAddress(physAddr)); // TODO ("00:00:00:00:00:00")
+ nbrBuilder.setOrigin(origin);
+
+ neighbors.add(nbrBuilder.build());
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void setForwarding(boolean fwd) {
+ ipv6Builder.setForwarding(fwd);
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void setMtu(long mtu) {
+ ipv6Builder.setMtu(mtu);
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public Ipv6 build() {
+ ipv6Builder.setAddress(addrs);
+ ipv6Builder.setNeighbor(neighbors);
+ return ipv6Builder.build();
+ }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/LoggingFuturesCallBack.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/LoggingFuturesCallBack.java new file mode 100644 index 000000000..35795cb7b --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/LoggingFuturesCallBack.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import com.google.common.util.concurrent.FutureCallback; +import org.slf4j.Logger; + +public class LoggingFuturesCallBack<V> implements FutureCallback<V> { + + private static Logger LOG; + private String message; + + public LoggingFuturesCallBack(String message, Logger log) { + this.message = message; + this.LOG = log; + } + + @Override + public void onFailure(Throwable err) { + LOG.warn(message,err); + + } + + @Override + public void onSuccess(V arg0) { + /* suppress success messages + if (arg0 == null) { + LOG.info("Success!"); + } else { + LOG.info("Success! {}", arg0); + } + */ + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poApiRequest.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poApiRequest.java new file mode 100644 index 000000000..f466adb53 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poApiRequest.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import java.math.BigInteger; +import java.net.InetAddress; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +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.TransactionCommitFailedException; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.SoftwareLoopback; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.AdminStatus; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state._interface.Statistics; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state._interface.StatisticsBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface2Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.IpAddressOrigin; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Counter32; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Counter64; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Gauge64; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStatisticsAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceStatisticsAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Ethernet.Duplex; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.EthernetBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.L2Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.Vxlan; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.VxlanBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.l2.interconnection.BridgeBasedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces.state._interface.l2.interconnection.XconnectBasedBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.vppjapi.vppBridgeDomainDetails; +import org.openvpp.vppjapi.vppBridgeDomainInterfaceDetails; +import org.openvpp.vppjapi.vppIPv4Address; +import org.openvpp.vppjapi.vppIPv6Address; +import org.openvpp.vppjapi.vppInterfaceCounters; +import org.openvpp.vppjapi.vppInterfaceDetails; +import org.openvpp.vppjapi.vppVxlanTunnelDetails; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.primitives.Ints; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +/* + * VPP API Class overriding interface details callback + */ +public class V3poApiRequest extends V3poRequest { + private static final Logger LOG = LoggerFactory.getLogger(V3poApiRequest.class); + public String ifNames = ""; + private VppPollOperDataImpl caller; + + public V3poApiRequest(VppPollOperDataImpl vppPollOperData) { + caller = vppPollOperData; + } + + private InstanceIdentifier<Interface> getStateInterfaceIid(String interfaceName) { + return InstanceIdentifier.create(InterfacesState.class).child(Interface.class, + new InterfaceKey(interfaceName)); + } + + private InstanceIdentifier<Interface2> getStateInterfaceIpId(InstanceIdentifier<Interface> iid) { + return iid.augmentation(Interface2.class); + } + + private InstanceIdentifier<Statistics> getStateInterfaceStatsId(InstanceIdentifier<Interface> iid) { + return iid.child(Statistics.class); + } + + private static Counter64 getCounter64(long num) { + return new Counter64(BigInteger.valueOf(num)); + } + + private static Counter32 getCounter32(long num) { + return new Counter32(num); + } + + private Statistics buildInterfaceStatistics(vppInterfaceCounters ifCounters) { + if (ifCounters == null) { + return null; + } + StatisticsBuilder statsBuilder = new StatisticsBuilder(); + + statsBuilder.setInBroadcastPkts(getCounter64(ifCounters.rxBroadcast)); + statsBuilder.setInDiscards(getCounter32(ifCounters.rxDiscard)); + statsBuilder.setInErrors(getCounter32(ifCounters.rxError)); + statsBuilder.setInMulticastPkts(getCounter64(ifCounters.rxMulticast)); + statsBuilder.setInOctets(getCounter64(ifCounters.rxOctets)); + statsBuilder.setInUnicastPkts(getCounter64(ifCounters.rxUnicast)); + statsBuilder.setInUnknownProtos(getCounter32(ifCounters.rxUnknownProto)); + + statsBuilder.setOutBroadcastPkts(getCounter64(ifCounters.txBroadcast)); + statsBuilder.setOutDiscards(getCounter32(ifCounters.txDiscard)); + statsBuilder.setOutErrors(getCounter32(ifCounters.txError)); + statsBuilder.setOutMulticastPkts(getCounter64(ifCounters.txMulticast)); + statsBuilder.setOutOctets(getCounter64(ifCounters.txOctets)); + statsBuilder.setOutUnicastPkts(getCounter64(ifCounters.txUnicast)); + + VppInterfaceStatisticsAugmentationBuilder statsAugBuilder = + new VppInterfaceStatisticsAugmentationBuilder(); + statsAugBuilder.setInErrorsMiss(getCounter64(ifCounters.rxMiss)); + statsAugBuilder.setInErrorsNoBuf(getCounter64(ifCounters.rxFifoFull)); // FIXME? Is this right? + statsAugBuilder.setOutDiscardsFifoFull(getCounter64(ifCounters.txFifoFull)); + + statsBuilder.addAugmentation(VppInterfaceStatisticsAugmentation.class, + statsAugBuilder.build()); + + return statsBuilder.build(); + } + + private static String getMacAddress(byte[] mac) { + StringBuilder sb = new StringBuilder(18); + for (byte b : mac) { + if (sb.length() > 0) { + sb.append(':'); + } + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + private static final Gauge64 vppSpeed0 = new Gauge64(BigInteger.ZERO); + private static final Gauge64 vppSpeed1 = new Gauge64(BigInteger.valueOf(10 * 1000000)); + private static final Gauge64 vppSpeed2 = new Gauge64(BigInteger.valueOf(100 * 1000000)); + private static final Gauge64 vppSpeed4 = new Gauge64(BigInteger.valueOf(1000 * 1000000)); + private static final Gauge64 vppSpeed8 = new Gauge64(BigInteger.valueOf(10000L * 1000000)); + private static final Gauge64 vppSpeed16 = new Gauge64(BigInteger.valueOf(40000L * 1000000)); + private static final Gauge64 vppSpeed32 = new Gauge64(BigInteger.valueOf(100000L * 1000000)); + + private static Gauge64 getSpeed(byte vppSpeed) { + switch (vppSpeed) { + case 1: return vppSpeed1; + case 2: return vppSpeed2; + case 4: return vppSpeed4; + case 8: return vppSpeed8; + case 16: return vppSpeed16; + case 32: return vppSpeed32; + default: return vppSpeed0; + } + } + + private static String ipv4IntToString(int ip) { + InetAddress addr = null; + byte[] bytes = Ints.toByteArray(ip); + try { + addr = InetAddress.getByAddress(bytes); + } catch (java.net.UnknownHostException e) { + e.printStackTrace(); + return null; + } + return addr.getHostAddress(); + } + + private Interface buildStateInterface(int ifIndex, + String interfaceName, + int supIfIndex, + byte[] physAddr, + byte adminUp, byte linkUp, + byte linkDuplex, byte linkSpeed, + int subId, byte subDot1ad, + byte subNumberOfTags, + int subOuterVlanId, + int subInnerVlanId, + byte subExactMatch, + byte subDefault, + byte subOuterVlanIdAny, + byte subInnerVlanIdAny, + int vtrOp, int vtrPushDot1q, + int vtrTag1, int vtrTag2, + Statistics stats) { + InterfaceBuilder ifBuilder = new InterfaceBuilder(); + java.lang.Class<? extends InterfaceType> ifType; + + // FIXME: missing types for virtualethernet, subinterface, tap interface etc + if (interfaceName.startsWith("loop")) { + ifType = SoftwareLoopback.class; + } else if (interfaceName.startsWith("vxlan_tunnel")) { + ifType = VxlanTunnel.class; + } else { + ifType = EthernetCsmacd.class; + } + ifBuilder.setName(interfaceName) + .setType(ifType) + .setAdminStatus((adminUp == 0 ? AdminStatus.Down : AdminStatus.Up)) + .setOperStatus((linkUp == 0 ? OperStatus.Down : OperStatus.Up)); +/* + DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = ImmutableNodes.mapEntryBuilder() + .withNodeIdentifier(new NodeIdentifierWithPredicates(Interface.QNAME, NAME_QNAME, interfaceName)); + builder.withChild(ImmutableNodes.leafNode(IF_TYPE, SoftwareLoopback.QNAME))*/ + + // subinterface? + if (ifIndex != supIfIndex) { + // TODO: get name and set + } + + if (physAddr != null) { + ifBuilder.setPhysAddress(new PhysAddress(getMacAddress(physAddr))); + } + ifBuilder.setSpeed(getSpeed(linkSpeed)); + + if (stats != null) { + ifBuilder.setStatistics(stats); + } + int bdId = this.bridgeDomainIdFromInterfaceName(interfaceName); + vppBridgeDomainDetails bd = (bdId != -1 ? this.getBridgeDomainDetails(bdId) : null); + + String bdName = null; + short splitHorizonGroup = 0; + boolean bvi = false; + + if (bd != null) { + bdName = bd.name; + for (int ifIdx = 0; ifIdx < bd.interfaces.length; ifIdx++) { + vppBridgeDomainInterfaceDetails bdIf = bd.interfaces[ifIdx]; + + if (bdIf.interfaceName != interfaceName) { + continue; + } + if (bd.bviInterfaceName == interfaceName) { + bvi = true; + } + splitHorizonGroup = (short)bdIf.splitHorizonGroup; + } + } + + VppInterfaceStateAugmentationBuilder vppIfStateAugBuilder = + new VppInterfaceStateAugmentationBuilder(); + + vppIfStateAugBuilder.setDescription(this.getInterfaceDescription(interfaceName)); + + setStateInterfaceL2(vppIfStateAugBuilder, bdId != -1, false, null, + bdName, splitHorizonGroup, bvi); + + if (EthernetCsmacd.class == ifType) { + setStateInterfaceEthernet(vppIfStateAugBuilder, linkDuplex == 2, + "ACME Inc.", 1234); + } + + vppVxlanTunnelDetails[] vxlanDet = this.vxlanTunnelDump(ifIndex); + if (null != vxlanDet && vxlanDet.length >= 1) { + setStateInterfaceVxlan(vppIfStateAugBuilder, vxlanDet[0].srcAddress, + vxlanDet[0].dstAddress, vxlanDet[0].vni, + vxlanDet[0].encapVrfId); + } + + ifBuilder.addAugmentation(VppInterfaceStateAugmentation.class, + vppIfStateAugBuilder.build()); + + InterfaceStateIpv4Builder ipv4Builder = new InterfaceStateIpv4Builder(); +// TODO ipv4Builder.setMtu(1234); + + InetAddress addr = null; + + vppIPv4Address[] ipv4Addrs = ipv4AddressDump(interfaceName); + if (ipv4Addrs != null) { + for (vppIPv4Address vppAddr : ipv4Addrs) { + if (null == vppAddr) { + LOG.error("ipv4 address structure in null"); + continue; + } + + // FIXME: vppIPv4Address and vppIPv6 address can be the same if both will use + // byte array for ip + byte[] bytes = Ints.toByteArray(vppAddr.ip); + try { + addr = InetAddress.getByAddress(bytes); + } catch (java.net.UnknownHostException e) { + e.printStackTrace(); + continue; + } + + ipv4Builder.addAddress(addr.getHostAddress(), vppAddr.prefixLength, IpAddressOrigin.Static); + } + } + + InterfaceStateIpv6Builder ipv6Builder = new InterfaceStateIpv6Builder(); +// TODO ipv6Builder.setMtu(1234); + + vppIPv6Address[] ipv6Addrs = ipv6AddressDump(interfaceName); + if (ipv6Addrs != null) { + for (vppIPv6Address vppAddr : ipv6Addrs) { + if (null == vppAddr) { + LOG.error("ipv6 address structure in null"); + continue; + } + + byte[] bytes = vppAddr.ip; + try { + addr = InetAddress.getByAddress(bytes); + } catch (java.net.UnknownHostException e) { + e.printStackTrace(); + continue; + } + + ipv6Builder.addAddress(addr.getHostAddress(), vppAddr.prefixLength, IpAddressOrigin.Static); + } + } + Interface2Builder ipBuilder = new Interface2Builder(); + + ipBuilder.setIpv4(ipv4Builder.build()); + ipBuilder.setIpv6(ipv6Builder.build()); + + ifBuilder.addAugmentation(Interface2.class, ipBuilder.build()); + + return ifBuilder.build(); + } + + private void setStateInterfaceL2( + VppInterfaceStateAugmentationBuilder augBuilder, + boolean isL2BridgeBased, boolean isXconnect, + String xconnectOutgoingInterface, + String bdName, short splitHorizonGroup, boolean bvi) { + + L2Builder l2Builder = new L2Builder(); + + if (isXconnect) { + l2Builder.setInterconnection( + new XconnectBasedBuilder() + .setXconnectOutgoingInterface(xconnectOutgoingInterface) + .build()); + } else if (isL2BridgeBased) { + l2Builder.setInterconnection( + new BridgeBasedBuilder() + .setBridgeDomain(bdName) + .setSplitHorizonGroup(splitHorizonGroup) + .setBridgedVirtualInterface(bvi) + .build()); + } + + augBuilder.setL2(l2Builder.build()); + } + + private void setStateInterfaceEthernet( + VppInterfaceStateAugmentationBuilder augBuilder, + boolean isFullDuplex, String manufacturerDesc, Integer mtu) { + + EthernetBuilder ethBuilder = new EthernetBuilder(); + ethBuilder.setDuplex((isFullDuplex ? Duplex.Full : Duplex.Half)) + .setManufacturerDescription(manufacturerDesc) + .setMtu(mtu); + + augBuilder.setEthernet(ethBuilder.build()); + } + + private void setStateInterfaceVxlan( + VppInterfaceStateAugmentationBuilder augBuilder, int srcAddress, + int dstAddress, int vni, int encapVrfId) { + + String srcAddressStr = ipv4IntToString(srcAddress); + String dstAddressStr = ipv4IntToString(dstAddress); + + VxlanBuilder vxlanBuilder = new VxlanBuilder(); + Vxlan vxlan = vxlanBuilder + .setSrc(new Ipv4AddressNoZone(srcAddressStr)) + .setDst(new Ipv4AddressNoZone(dstAddressStr)) + .setVni((long)vni) + .setEncapVrfId((long)encapVrfId) + .build(); + + augBuilder.setVxlan(vxlan); + } + + private void writeToIfState(InstanceIdentifier<Interface> iid, + Interface intf) { + DataBroker db = caller.getDataBroker(); + WriteTransaction transaction = db.newWriteOnlyTransaction(); + // TODO: how to delete existing interfaces that disappeared? (reset it before each dumpInterfaces call?) + + /*LOG.info("VPPOPER-INFO: Adding interface " + intf.getName() + + " to oper DataStore.");*/ + transaction.put(LogicalDatastoreType.OPERATIONAL, iid, intf); + + CheckedFuture<Void, TransactionCommitFailedException> future = + transaction.submit(); + Futures.addCallback(future, new LoggingFuturesCallBack<Void>( + "VPPOPER-WARNING: Failed to write " + + "interface to ietf-interfaces state", LOG)); + } + + private void processInterfaces(vppInterfaceDetails[] ifaces) { + for (vppInterfaceDetails swIf : ifaces) { + interfaceDetails(swIf); + } + } + + /** + * TODO-ADD-JAVADOC. + */ + public void swInterfaceDumpAll() { + vppInterfaceDetails[] ifaces; + + ifaces = swInterfaceDump((byte) 1, "Ether".getBytes()); + processInterfaces(ifaces); + + ifaces = swInterfaceDump((byte) 1, "lo".getBytes()); + processInterfaces(ifaces); + + ifaces = swInterfaceDump((byte) 1, "vxlan".getBytes()); + processInterfaces(ifaces); + + ifaces = swInterfaceDump((byte) 1, "l2tpv3_tunnel".getBytes()); + processInterfaces(ifaces); + + ifaces = swInterfaceDump((byte) 1, "tap".getBytes()); + processInterfaces(ifaces); + } + + private void interfaceDetails(vppInterfaceDetails swIf) { + /*LOG.info("Got interface {} (idx: {}) adminUp: {} linkUp: {} duplex: {} speed: {} subId: {}", + swIf.interfaceName, swIf.ifIndex, swIf.adminUp, swIf.linkUp, swIf.linkDuplex, swIf.linkSpeed, swIf.subId);*/ + + vppInterfaceCounters ifCounters = getInterfaceCounters(swIf.ifIndex); + + InstanceIdentifier<Interface> iid = getStateInterfaceIid(swIf.interfaceName); + + Statistics stats = buildInterfaceStatistics(ifCounters); + + Interface intf = buildStateInterface(swIf.ifIndex, swIf.interfaceName, + swIf.supIfIndex, swIf.physAddr, + swIf.adminUp, swIf.linkUp, + swIf.linkDuplex, swIf.linkSpeed, + swIf.subId, swIf.subDot1ad, + swIf.subNumberOfTags, + swIf.subOuterVlanId, + swIf.subInnerVlanId, + swIf.subExactMatch, swIf.subDefault, + swIf.subOuterVlanIdAny, + swIf.subInnerVlanIdAny, + swIf.vtrOp, swIf.vtrPushDot1q, + swIf.vtrTag1, swIf.vtrTag2, stats); + writeToIfState(iid, intf); + + ifNames += " " + swIf.interfaceName; + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java new file mode 100644 index 000000000..3b546f16f --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poProvider.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +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.TransactionCommitFailedException; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration; +import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesStateBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4Builder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.AddressKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLengthBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.V3poService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.EthernetBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2Builder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.RoutingBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBasedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomainKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.vppjapi.vppApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; + +public class V3poProvider implements BindingAwareProvider, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(V3poProvider.class); + private RpcRegistration<V3poService> v3poService; + private VppIetfInterfaceListener vppInterfaceListener; + private VppBridgeDomainListener vppBridgeDomainListener; + private static final vppApi api = new vppApi(); + private static DataBroker db; + VppPollOperDataImpl vppPollOperData; + + private void writeToBridgeDomain(String bdName, Boolean flood, + Boolean forward, Boolean learn, + Boolean unknownUnicastFlood, + Boolean arpTermination) { + + BridgeDomainBuilder bdBuilder = new BridgeDomainBuilder(); + bdBuilder.setName(bdName); + bdBuilder.setFlood(flood); + bdBuilder.setForward(forward); + bdBuilder.setLearn(learn); + bdBuilder.setUnknownUnicastFlood(unknownUnicastFlood); + bdBuilder.setArpTermination(arpTermination); + + LOG.info("VPPCFG-INFO: Adding Bridge Domain " + bdName + " to DataStore."); + InstanceIdentifier<BridgeDomain> iid = + InstanceIdentifier.create(Vpp.class) + .child(BridgeDomains.class) + .child(BridgeDomain.class, new BridgeDomainKey(bdName)); + WriteTransaction transaction = db.newWriteOnlyTransaction(); + transaction.put(LogicalDatastoreType.CONFIGURATION, iid, bdBuilder.build()); + CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit(); + Futures.addCallback(future, new LoggingFuturesCallBack<Void>( + "VPPCFG-WARNING: Failed to write bridge domain " + bdName + " to Bridge Domains", LOG)); + } + + private void writeIpv4AddressToInterface(String name, Ipv4AddressNoZone ipv4Addr, short plen) { + AddressKey addrKey = new AddressKey(ipv4Addr); + AddressBuilder addrBuilder = new AddressBuilder(); + PrefixLength prefixLen = new PrefixLengthBuilder().setPrefixLength(plen).build(); + addrBuilder.setSubnet(prefixLen); + addrBuilder.setIp(new Ipv4AddressNoZone(ipv4Addr)); + addrBuilder.setKey(addrKey); + + List<Address> addrs = new ArrayList<Address>(); + addrs.add(addrBuilder.build()); + + Ipv4 ip4 = new Ipv4Builder().setAddress(addrs).build(); + Interface1Builder if1Builder = new Interface1Builder(); + if1Builder.setIpv4(ip4); + + InterfaceBuilder ifBuilder = new InterfaceBuilder(); + ifBuilder.setName(name); + ifBuilder.addAugmentation(Interface1.class, if1Builder.build()); + + LOG.info("VPPCFG-INFO: Adding ipv4 address {} to interface {} to DataStore.", ipv4Addr, name); + InstanceIdentifier<Interface> iid = + InstanceIdentifier.create(Interfaces.class) + .child(Interface.class, new InterfaceKey(name)); + WriteTransaction transaction = db.newWriteOnlyTransaction(); + transaction.put(LogicalDatastoreType.CONFIGURATION, iid, ifBuilder.build()); + CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit(); + Futures.addCallback(future, new LoggingFuturesCallBack<Void>( + "VPPCFG-WARNING: Failed to write " + name + "interface to ietf-interfaces", LOG)); + } + + private void writeToInterface(String name, String description, + Boolean enabled, String bdName, int vrfId) { + VppInterfaceAugmentationBuilder ifAugBuilder = new VppInterfaceAugmentationBuilder(); + + EthernetBuilder ethBuilder = new EthernetBuilder(); + ethBuilder.setMtu(1234); + ifAugBuilder.setEthernet(ethBuilder.build()); + + if (bdName != null) { + BridgeBasedBuilder bridgeBuilder = new BridgeBasedBuilder(); + bridgeBuilder.setBridgeDomain(bdName); + bridgeBuilder.setSplitHorizonGroup((short)0); + bridgeBuilder.setBridgedVirtualInterface(false); + + L2Builder l2Builder = new L2Builder(); + l2Builder.setInterconnection(bridgeBuilder.build()); + ifAugBuilder.setL2(l2Builder.build()); + } + + if (vrfId > 0) { + RoutingBuilder rtBuilder = new RoutingBuilder(); + rtBuilder.setVrfId(new Long(vrfId)); + ifAugBuilder.setRouting(rtBuilder.build()); + } + + InterfaceBuilder ifBuilder = new InterfaceBuilder(); + ifBuilder.setName(name); + ifBuilder.setDescription(description); + ifBuilder.setType(EthernetCsmacd.class); + ifBuilder.setEnabled(enabled); + ifBuilder.setLinkUpDownTrapEnable(Interface.LinkUpDownTrapEnable.Disabled); + + ifBuilder.addAugmentation(VppInterfaceAugmentation.class, ifAugBuilder.build()); + + LOG.info("VPPCFG-INFO: Adding interface " + name + " to DataStore."); + InstanceIdentifier<Interface> iid = InstanceIdentifier.create(Interfaces.class) + .child(Interface.class, new InterfaceKey(name)); + WriteTransaction transaction = db.newWriteOnlyTransaction(); + transaction.put(LogicalDatastoreType.CONFIGURATION, iid, ifBuilder.build()); + CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit(); + Futures.addCallback(future, new LoggingFuturesCallBack<Void>( + "VPPCFG-WARNING: Failed to write " + name + "interface to ietf-interfaces", LOG)); + } + + private void initializeVppConfig() { + + WriteTransaction transaction = db.newWriteOnlyTransaction(); + InstanceIdentifier<Vpp> viid = InstanceIdentifier.create(Vpp.class); + Vpp vpp = new VppBuilder().build(); + transaction.put(LogicalDatastoreType.CONFIGURATION, viid, vpp); + CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit(); + Futures.addCallback(future, new + LoggingFuturesCallBack<>("VPPCFG-WARNING: Failed to create Vpp " + + "configuration db.", + LOG)); + vppBridgeDomainListener = new VppBridgeDomainListener(db, api); + + LOG.info("VPPCFG-INFO: Preparing to initialize the IETF Interface " + "list configuration db."); + transaction = db.newWriteOnlyTransaction(); + InstanceIdentifier<Interfaces> iid = InstanceIdentifier.create(Interfaces.class); + Interfaces intf = new InterfacesBuilder().build(); + transaction.put(LogicalDatastoreType.CONFIGURATION, iid, intf); + future = transaction.submit(); + Futures.addCallback(future, new + LoggingFuturesCallBack<>("VPPCFG-WARNING: Failed to create IETF " + + "Interface list configuration db.", + LOG)); + vppInterfaceListener = new VppIetfInterfaceListener(db, api); + + /* DAW-DEBUG: + try { + int wait = 3; + LOG.info("VPPCFG-INFO: Sleeping for {} seconds...", wait); + TimeUnit.SECONDS.sleep(wait); + } catch (InterruptedException e) { + LOG.info("VPPCFG-INFO: Sleep Interrupted!"); + } + LOG.info("VPPCFG-INFO: Nap complete. I feel much better now."); + */ + + /* Test DataChangeListener by writing to db */ + writeToBridgeDomain("CocaCola", true /*flood*/, true /*forward*/, + true /*learn*/, true /*uuFlood*/, + false /*arpTermination*/); + writeToBridgeDomain("PepsiCola", true /*flood*/, true /*forward*/, + true /*learn*/, true /*uuFlood*/, + false /*arpTermination*/); + + + writeToInterface("TenGigabitEthernet86/0/1", + "Physical 10GbE Interface (Transport)", + true, null, 7); + writeToInterface("TenGigabitEthernet86/0/0", "Physical 10GbE Interface", + true, "CocaCola", 0); + writeToInterface("GigabitEthernet8/0/1", "Physical 1GbE Interface", + true, "PepsiCola", 0); + + /* + writeIpv4AddressToInterface("GigabitEthernet86/0/1", + new Ipv4AddressNoZone("10.10.10.10"), + (short)24); + writeIpv4AddressToInterface("GigabitEthernet86/0/1", + new Ipv4AddressNoZone("11.11.11.10"), + (short)24); + writeIpv4AddressToInterface("GigabitEthernet86/0/1", + new Ipv4AddressNoZone("11.11.11.10"), + (short)24); + */ + /* Interfaces on virtual testbed VMs (e.g. js-cluster-1) */ + writeToBridgeDomain("Balvenie", true /*flood*/, true /*forward*/, + true /*learn*/, true /*uuFlood*/, + false /*arpTermination*/); + writeToBridgeDomain("Laphroaig", true /*flood*/, true /*forward*/, + true /*learn*/, true /*uuFlood*/, + false /*arpTermination*/); + writeToBridgeDomain("Glenfiddich", true /*flood*/, true /*forward*/, + true /*learn*/, true /*uuFlood*/, + false /*arpTermination*/); + writeToBridgeDomain("Macallan", true /*flood*/, true /*forward*/, + true /*learn*/, true /*uuFlood*/, + false /*arpTermination*/); + + writeToInterface("GigabitEthernet2/2/0", "Physical 1GbE Interface", + true, "Balvenie", 0); + writeToInterface("GigabitEthernet2/3/0", "Physical 1GbE Interface", + true, "Laphroaig", 0); + writeToInterface("GigabitEthernet2/4/0", "Physical 1GbE Interface", + true, "Glenfiddich", 0); + writeToInterface("GigabitEthernet2/5/0", "Physical 1GbE Interface", + true, "Macallan", 0); + writeToInterface("GigabitEthernet2/6/0", + "Physical 1GbE Interface (Transport)", + true, null, 7); + } + + /* operational data */ + + private void initVppOperational() { + /* + * List<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf. + * interfaces.rev140508.interfaces.state.Interface> ifaces = new + * ArrayList<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang. + * ietf.interfaces.rev140508.interfaces.state.Interface>(); + */ + LOG.info("VPPOPER-INFO: Preparing to initialize the IETF Interface " + "state list operational db."); + InterfacesState ifsState = new InterfacesStateBuilder().build(); + WriteTransaction tx = db.newWriteOnlyTransaction(); + InstanceIdentifier<InterfacesState> isid = InstanceIdentifier.builder(InterfacesState.class).build(); + tx.put(LogicalDatastoreType.OPERATIONAL, isid, ifsState); + Futures.addCallback(tx.submit(), new LoggingFuturesCallBack<>( + "VPPOPER-WARNING: Failed to create IETF " + "Interface state list operational db.", LOG)); + } + + private void startOperationalUpdateTimer() { + Timer timer = new Timer(); + + // fire task after 1 second and then repeat each 10 seconds + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + vppPollOperData.updateOperational(); + } + }, 1000, 10000); + } + + @Override + public void onSessionInitiated(ProviderContext session) { + LOG.info("VPP-INFO: V3poProvider Session Initiated"); + int rv = api.clientConnect("v3poODL"); + LOG.info("VPP-INFO: VPP api client connection return value = {}", rv); + if (rv != 0) { + LOG.error("VPP-ERROR: VPP api client connection failed: return value = {}", rv); + return; + } + db = session.getSALService(DataBroker.class); + initializeVppConfig(); + initVppOperational(); + + vppPollOperData = new VppPollOperDataImpl(db); + v3poService = session.addRpcImplementation(V3poService.class, + vppPollOperData); + startOperationalUpdateTimer(); + } + + @Override + public void close() throws Exception { + LOG.info("VPP-INFO: V3poProvider Closed"); + if (v3poService != null) { + v3poService.close(); + } + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poRequest.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poRequest.java new file mode 100644 index 000000000..900161577 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/V3poRequest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import org.openvpp.vppjapi.vppApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* + * Abstract class overriding all callbacks with default error message + */ +public abstract class V3poRequest extends vppApi { + private static final Logger LOG = LoggerFactory.getLogger(V3poRequest.class); +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppBridgeDomainListener.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppBridgeDomainListener.java new file mode 100644 index 000000000..94d8df7c7 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppBridgeDomainListener.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import java.util.Collection; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.Vpp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.BridgeDomains; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.bridge.domains.BridgeDomain; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.vppjapi.vppApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VppBridgeDomainListener implements DataTreeChangeListener<BridgeDomain>, + AutoCloseable { + private static final Logger LOG = + LoggerFactory.getLogger(VppBridgeDomainListener.class); + private ListenerRegistration<VppBridgeDomainListener> registration; + private DataBroker db; + private vppApi api; + + private enum DataChangeType { + CREATE, UPDATE, DELETE + } + + /** + * TODO-ADD-JAVADOC. + */ + public VppBridgeDomainListener(DataBroker db, vppApi api) { + this.db = db; + this.api = api; + InstanceIdentifier<BridgeDomain> iid = InstanceIdentifier + .create(Vpp.class) + .child(BridgeDomains.class) + .child(BridgeDomain.class); + LOG.info("VPPCFG-INFO: Register listener for VPP Bridge Domain data changes"); + + DataTreeIdentifier<BridgeDomain> path = + new DataTreeIdentifier<BridgeDomain>(LogicalDatastoreType.CONFIGURATION, iid); + registration = this.db.registerDataTreeChangeListener(path, this); + } + + @Override + public void onDataTreeChanged(Collection<DataTreeModification<BridgeDomain>> changes) { + + for (DataTreeModification<BridgeDomain> change: changes) { + InstanceIdentifier<BridgeDomain> iid = change.getRootPath().getRootIdentifier(); + DataObjectModification<BridgeDomain> changeDiff = change.getRootNode(); + + switch (changeDiff.getModificationType()) { + case SUBTREE_MODIFIED: + case WRITE: + // create, modify or replace + createOrUpdateBridgeDomain(changeDiff); + break; + case DELETE: + deleteBridgeDomain(changeDiff); + break; + default: + LOG.info("Unsupported change type {} for {}", + changeDiff.getModificationType(), iid); + } + } + } + + // handles only CREATE and UPDATE calls + private void vppSetBridgeDomain(BridgeDomain bridgeDomain, DataChangeType type, + BridgeDomain originalBridgeDomain) { + int rv = -77; + int cnt = 0; + String bdName = bridgeDomain.getName(); + int bdId = api.findOrAddBridgeDomainId(bdName); + + LOG.info("VPPCFG-INFO: {} <bridgeDomain>", type); + LOG.info("VPPCFG-INFO: Name: " + bdName); + LOG.info("VPPCFG-INFO: Flood: {} ", bridgeDomain.isFlood()); + LOG.info("VPPCFG-INFO: Forward: {} ", bridgeDomain.isForward()); + LOG.info("VPPCFG-INFO: Learn: {} ", bridgeDomain.isLearn()); + LOG.info("VPPCFG-INFO: UnknownUnicastFlood: {} ", + bridgeDomain.isUnknownUnicastFlood()); + LOG.info("VPPCFG-INFO: ArpTermination: {} ", + bridgeDomain.isArpTermination()); + LOG.info("VPPCFG-INFO: {} </bridgeDomain>", type); + + switch (type) { + case CREATE: + case UPDATE: + byte flood = bridgeDomain.isFlood() ? (byte) 1 : (byte) 0; + byte forward = bridgeDomain.isForward() ? (byte) 1 : (byte) 0; + byte learn = bridgeDomain.isLearn() ? (byte) 1 : (byte) 0; + byte uuf = bridgeDomain.isUnknownUnicastFlood() ? (byte) 1 : (byte) 0; + byte arpTerm = bridgeDomain.isArpTermination() ? (byte) 1 : (byte) 0; + if ((bdId == -1) || (bdId == 0)) { + LOG.warn("VPPCFG-WARNING: Bridge Domain create/lookup failed" + + " (bdId = {})! Ignoring vppSetBridgeDomain request {}", + bdId, type); + return; + } else { + int ctxId = api.bridgeDomainAddDel(bdId, flood, forward, + learn, uuf, arpTerm, + (byte) 1 /* isAdd */); + LOG.info("VPPCFG-INFO: {} api.bridgeDomainAddDel({} ({})) " + + "ctxId = {}", type, bdName, bdId, ctxId); + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + LOG.info("VPPCFG-INFO: {} api.bridgeDomainAddDel({} ({})) " + + "retval {} after {} tries.", + type, bdName, bdId, rv, cnt); + + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: {} api.bridgeDomainAddDel({}" + + " ({})) failed: retval {}!", + type, bdName, bdId, rv); + /* DAW-FIXME: throw exception on failure? */ + } + } + break; + default: + LOG.warn("VPPCFG-WARNING: Unknown DataChangeType {}! " + + "Ignoring vppSetBridgeDomain request", type); + return; + } + bdId = api.bridgeDomainIdFromName(bdName); + LOG.info("VPPCFG-INFO: {} api.bridgeDomainIdFromName({}) = {}", + type, bdName, bdId); + } + + private void createOrUpdateBridgeDomain(DataObjectModification<BridgeDomain> changeDiff) { + if (changeDiff.getDataBefore() == null) { + vppSetBridgeDomain(changeDiff.getDataAfter(), + DataChangeType.CREATE, null); + } else { + vppSetBridgeDomain(changeDiff.getDataAfter(), + DataChangeType.UPDATE, + changeDiff.getDataBefore()); + } + } + + // handles DELETE calls + private void deleteBridgeDomain(DataObjectModification<BridgeDomain> changeDiff) { + DataChangeType type = DataChangeType.DELETE; + BridgeDomain bridgeDomain = changeDiff.getDataBefore(); + String bdName = bridgeDomain.getName(); + int rv = -77; + int cnt = 0; + + LOG.info("VPPCFG-INFO: {} <bridgeDomain>", type); + LOG.info("VPPCFG-INFO: Name: " + bdName); + LOG.info("VPPCFG-INFO: Flood: {} ", bridgeDomain.isFlood()); + LOG.info("VPPCFG-INFO: Forward: {} ", bridgeDomain.isForward()); + LOG.info("VPPCFG-INFO: Learn: {} ", bridgeDomain.isLearn()); + LOG.info("VPPCFG-INFO: UnknownUnicastFlood: {} ", + bridgeDomain.isUnknownUnicastFlood()); + LOG.info("VPPCFG-INFO: ArpTermination: {} ", + bridgeDomain.isArpTermination()); + LOG.info("VPPCFG-INFO: {} </bridgeDomain>", type); + + int bdId = api.findOrAddBridgeDomainId(bdName); + if ((bdId == -1) || (bdId == 0)) { + LOG.warn("VPPCFG-WARNING: Unknown Bridge Domain {} " + + " (bdId = {})! Ignoring vppSetBridgeDomain request {}", + bdName, bdId, type); + return; + } else { + int ctxId = api.bridgeDomainAddDel(bdId, (byte) 0 /* flood */, + (byte) 0 /* forward */, + (byte) 0 /* learn */, + (byte) 0 /* uuf */, + (byte) 0 /* arpTerm */, + (byte) 0 /* isAdd */); + LOG.info("VPPCFG-INFO: {} api.bridgeDomainAddDel({} ({})) " + + "ctxId = {}", type, bdName, bdId, ctxId); + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + LOG.info("VPPCFG-INFO: {} api.bridgeDomainAddDel({} ({})) " + + "retval {} after {} tries.", + type, bdName, bdId, rv, cnt); + + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: {} api.bridgeDomainAddDel({} ({}))" + + " failed: retval {}!", type, bdName, bdId, rv); + /* DAW-FIXME: throw exception on failure? */ + } + } + } + + @Override + public void close() throws Exception { + registration.close(); + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppIetfInterfaceListener.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppIetfInterfaceListener.java new file mode 100644 index 000000000..7e6aa50f1 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppIetfInterfaceListener.java @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import java.util.Collection; + +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.EthernetCsmacd; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.Interface1; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.Ipv6; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.Address; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.Subnet; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.Netmask; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ip.rev140616.interfaces._interface.ipv4.address.subnet.PrefixLength; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VxlanTunnel; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Routing; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Vxlan; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.Interconnection; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.BridgeBased; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.l2.interconnection.XconnectBased; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.vppjapi.vppApi; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VppIetfInterfaceListener implements DataTreeChangeListener<Interface>, AutoCloseable { + private static final Logger LOG = + LoggerFactory.getLogger(VppIetfInterfaceListener.class); + + private ListenerRegistration<VppIetfInterfaceListener> registration; + private DataBroker db; + private vppApi api; + + private enum DataChangeType { + CREATE, UPDATE, DELETE + } + + /** + * TODO-ADD-JAVADOC. + */ + public VppIetfInterfaceListener(DataBroker db, vppApi api) { + this.db = db; + this.api = api; + InstanceIdentifier<Interface> iid = InstanceIdentifier + .create(Interfaces.class) + .child(Interface.class); + LOG.info("VPPCFG-INFO: Register listener for VPP Ietf Interface data changes"); + + DataTreeIdentifier<Interface> path = + new DataTreeIdentifier<Interface>(LogicalDatastoreType.CONFIGURATION, iid); + + registration = this.db.registerDataTreeChangeListener(path, this); + } + + @Override + public void onDataTreeChanged(Collection<DataTreeModification<Interface>> changes) { + LOG.info("VPPCFG-INFO: swIf onDataTreeChanged()"); + for (DataTreeModification<Interface> change: changes) { + InstanceIdentifier<Interface> iid = change.getRootPath().getRootIdentifier(); + DataObjectModification<Interface> changeDiff = change.getRootNode(); + + switch (changeDiff.getModificationType()) { + case SUBTREE_MODIFIED: + case WRITE: + // create, modify or replace + createOrUpdateInterface(changeDiff); + break; + case DELETE: + deleteInterface(changeDiff); + break; + default: + LOG.info("Unsupported change type {} for {}", + changeDiff.getModificationType(), iid); + } + } + } + + private void vppSetVppInterfaceEthernetAndL2(int swIfIndex, + String swIfName, + VppInterfaceAugmentation + vppInterface) { + int ctxId = 0; + int rv = -77; + int cnt = 0; + String apiName = ""; + + LOG.info("VPPCFG-INFO: <vppSetVppInterfaceEthernetAndL2>"); + LOG.info("VPPCFG-INFO: swIfIndex = {}", swIfIndex); + LOG.info("VPPCFG-INFO: swIfName = {}", swIfName); + LOG.info("VPPCFG-INFO: vppInterface = {}", vppInterface); + LOG.info("VPPCFG-INFO: </vppSetVppInterfaceEthernetAndL2>"); + if (vppInterface != null) { + Ethernet vppEth = vppInterface.getEthernet(); + if (vppEth != null) { + LOG.info("VPPCFG-INFO: {} Ethernet MTU = {}", + swIfName, vppEth.getMtu()); + /* DAW-FIXME: Need vpe-api msg to configure the Ethernet MTU */ + } + + L2 vppL2 = vppInterface.getL2(); + if (vppL2 != null) { + Interconnection ic = vppL2.getInterconnection(); + if (ic instanceof XconnectBased) { + XconnectBased xc = (XconnectBased) ic; + String outSwIfName = xc.getXconnectOutgoingInterface(); + LOG.info("VPPCFG-INFO: XconnectBased"); + LOG.info("VPPCFG-INFO: XconnectOutgoingInterface = {}", + outSwIfName); + + int outSwIfIndex = api.swIfIndexFromName(outSwIfName); + if (swIfIndex != -1) { + apiName = "api.swInterfaceSetL2Xconnect"; + ctxId = + api.swInterfaceSetL2Xconnect(swIfIndex, + outSwIfIndex, + (byte)1 /* enable */); + LOG.info("VPPCFG-INFO: {}() : outSwIfName = {}, " + + "outSwIfIndex = {}, ctxId = {}", apiName, + outSwIfName, outSwIfIndex, ctxId); + cnt = 0; + rv = -77; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: {}() ctxId = {} failed:" + + " retval = {}!", apiName, ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval = {}" + + " after {} tries.", apiName, ctxId, + rv, cnt); + } + + } else { + LOG.warn("VPPCFG-WARNING: Unknown Outgoing Interface ({})" + + " specified", outSwIfName); + } + + } else if (ic instanceof BridgeBased) { + BridgeBased bb = (BridgeBased) ic; + String bdName = bb.getBridgeDomain(); + int bdId = api.bridgeDomainIdFromName(bdName); + if (bdId > 0) { + byte bvi = + bb.isBridgedVirtualInterface() ? (byte) 1 : (byte) 0; + byte shg = bb.getSplitHorizonGroup().byteValue(); + + LOG.info("VPPCFG-INFO: BridgeBased"); + LOG.info("VPPCFG-INFO: BridgeDomain = {}, bdId = {}", + bdName, bdId); + LOG.info("VPPCFG-INFO: SplitHorizonGroup = {}", + shg); + LOG.info("VPPCFG-INFO: isBridgedVirtualInterface = {}", + bvi); + + apiName = "api.swInterfaceSetL2Bridge"; + ctxId = + api.swInterfaceSetL2Bridge(swIfIndex, + bdId, shg, bvi, + (byte)1 /* enable */); + LOG.info("VPPCFG-INFO: {}() : bdId = {}, shg = {}, " + + "bvi = {}, ctxId = {}", apiName, bdId, + shg, bvi, ctxId); + cnt = 0; + rv = -77; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING:{}() ctxId = {} failed: " + + "retval = {}!", apiName, ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval = {}" + + " after {} tries.", apiName, ctxId, + rv, cnt); + } + + } else { + LOG.error("VPPCFG-ERROR: Bridge Domain {} does not exist!", + bdName); + } + + } else { + LOG.error("VPPCFG-ERROR: unknonwn interconnection type!"); + } + } + } + } + + /** + * TODO-ADD-JAVADOC. + */ + public static int parseIp(String address) { + int result = 0; + + // iterate over each octet + for (String part : address.split("\\.")) { + // shift the previously parsed bits over by 1 byte + result = result << 8; + // set the low order bits to the current octet + result |= Integer.parseInt(part); + } + return result; + } + + private void createVxlanTunnel(String swIfName, Vxlan vxlan) { + Ipv4Address srcAddress = vxlan.getSrc(); + Ipv4Address dstAddress = vxlan.getDst(); + + int srcAddr = parseIp(srcAddress.getValue()); + int dstAddr = parseIp(dstAddress.getValue()); + int encapVrfId = vxlan.getEncapVrfId().intValue(); + int vni = vxlan.getVni().intValue(); + + int ctxId = api.vxlanAddDelTunnel((byte)1 /* is add */, srcAddr, dstAddr, encapVrfId, -1, vni); + String apiName = "api.vxlanAddDelTunnel"; + LOG.info("VPPCFG-INFO: {}({}, src: {}, dst: {} enabled ([]), ...) : " + + "ctxId = {}", apiName, swIfName, srcAddress.getValue(), + dstAddress.getValue(), ctxId); + + /* need to wait for creation of interface */ + int rv = -77; + int cnt = 0; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: {}() ctxId = {} failed: retval = {}!", apiName, ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval = {} after {} tries.", apiName, ctxId, rv, cnt); + } + } + + private byte [] ipv4AddressNoZoneToArray(Ipv4AddressNoZone ipv4Addr) { + byte [] retval = new byte [4]; + String addr = ipv4Addr.getValue().toString(); + String [] dots = addr.split("\\."); + + for (int d = 3; d >= 0; d--) { + retval[d] = (byte)(Short.parseShort(dots[3 - d]) & 0xff); + } + return retval; + } + + private void vppSetInterface(Interface swIf, DataChangeType type, + Interface originalIf) { + VppInterfaceAugmentation vppInterface = + swIf.getAugmentation(VppInterfaceAugmentation.class); + int ctxId = 0; + int cnt = 0; + int rv = -77; + String apiName = ""; + + /* DAW-FIXME: If type == UPDATE, use originalDataObject to get + * state of api parameters which have not been changed. + * For now, all parameters must be set at the same time. + */ + LOG.info("VPPCFG-INFO: {} <swIf>", type); + LOG.info("VPPCFG-INFO: Name: " + swIf.getName()); + LOG.info("VPPCFG-INFO: Desc: " + swIf.getDescription()); + java.lang.Class<? extends + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType> + ifType = swIf.getType(); + if (ifType != null) { + LOG.info("VPPCFG-INFO: Type: " + swIf.getType().getSimpleName()); + } + LOG.info("VPPCFG-INFO: {} </swIf>", type); + + String swIfName = swIf.getName(); + int swIfIndex = api.swIfIndexFromName(swIfName); + + if ((ifType != null) && ifType.isAssignableFrom(EthernetCsmacd.class)) { + if (swIfIndex != -1) { + LOG.info("VPPCFG-INFO: {} : swIfIndex = {}", + swIfName, swIfIndex); + + /* set vpp ethernet and l2 containers */ + vppSetVppInterfaceEthernetAndL2(swIfIndex, swIfName, + vppInterface); + + byte enabled = swIf.isEnabled() ? (byte) 1 : (byte) 0; + apiName = "api.swInterfaceSetFlags"; + ctxId = api.swInterfaceSetFlags((int)swIfIndex, + (byte)enabled, + (byte)enabled, + (byte)0 /* deleted */); + LOG.info("VPPCFG-INFO: {}({} ([]), enabled ([]), ...) : " + + "ctxId = {}", apiName, swIfName, swIfIndex, + enabled, ctxId); + cnt = 0; + rv = -77; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: api.swInterfaceSetFlags() " + + "ctxId = {} failed: retval = {}!", ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval = {} after" + + " {} tries.", apiName, ctxId, rv, cnt); + } + } else { + LOG.error("VPPCFG-ERROR: {} not found!", + swIf.getType().getSimpleName()); + LOG.error("VPPCFG-ERROR: cannot create {} type interfaces : " + + "ignoring create request for {} !", + swIf.getType().getSimpleName(), swIf.getName()); + } + + } else if ((ifType != null) + && ifType.isAssignableFrom(VxlanTunnel.class)) { + LOG.info("VPPCFG-INFO: VxLAN tunnel configuration"); + + // TODO: check name of interface, make use of renumber to change vpp + // interface name to desired one + + if (swIfIndex != -1) { + // interface exists in vpp + if (type == DataChangeType.DELETE) { + // TODO + } else { + // TODO + Vxlan vxlan = vppInterface.getVxlan(); + + LOG.info("Vxlan update: {}", vxlan); + } + } else { + // interface does not exist in vpp + if (type == DataChangeType.DELETE) { + // cannot delete non existent interface + LOG.error("VPPCFG-ERROR: Cannot delete non existing interface ({})", swIf.getName()); + } else { + Vxlan vxlan = vppInterface.getVxlan(); + + createVxlanTunnel(swIfName, vxlan); + + // refresh interfaces to be able to get ifIndex + api.swInterfaceDump((byte)1, "vxlan".getBytes()); + + int newSwIfIndex = api.swIfIndexFromName(swIfName); + + /* set vpp ethernet and l2 containers */ + vppSetVppInterfaceEthernetAndL2(newSwIfIndex, + swIfName, + vppInterface); + + byte enabled = swIf.isEnabled() ? (byte) 1 : (byte) 0; + ctxId = api.swInterfaceSetFlags((int)newSwIfIndex, + (byte)enabled, + (byte)enabled, + (byte)0 /* deleted */); + + swIfIndex = newSwIfIndex; + + apiName = "api.swInterfaceSetFlags"; + LOG.info("VPPCFG-INFO: {}({} ({}), enabled ({}), ...) :" + + " ctxId = {}", apiName, swIfName, + newSwIfIndex, enabled, ctxId); + cnt = 0; + rv = -77; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: {}() ctxId = {} failed: retval = {}!", apiName, ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval = {} after {} tries.", apiName, ctxId, rv, cnt); + } + } + } + + /* DAW-FIXME: Add additional interface types here. + * + * } else if ((ifType != null) && ifType.isAssignableFrom(*.class)) { + */ + } else if (ifType != null) { + LOG.error("VPPCFG-ERROR: Unsupported interface type ({}) : {}" + + " cannot be created!", ifType.getSimpleName(), + swIf.getName()); + } + + if (swIfIndex == -1) { + LOG.warn("VPPCFG-INFO: Unknown Interface {}", swIfName); + return; + } + + if (swIf.getDescription() != null) { + api.setInterfaceDescription(swIfName, swIf.getDescription()); + } else { + api.setInterfaceDescription(swIfName, ""); + } + Routing rt = vppInterface.getRouting(); + int vrfId = (rt != null) ? rt.getVrfId().intValue() : 0; + LOG.info("VPPCFG-INFO: vrfId = {}", vrfId); + if (vrfId > 0) { + apiName = "api.swInterfaceSetTable"; + ctxId = api.swInterfaceSetTable((int)swIfIndex, + (byte)0, /* isIpv6 */ + vrfId); + LOG.info("VPPCFG-INFO: {}({} ([]), 0 /* isIpv6 */, {} /* vrfId */)" + + " : ctxId = {}", apiName, swIfName, swIfIndex, + vrfId, ctxId); + cnt = 0; + rv = -77; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: api.swInterfaceSetTable() ctxId = {} failed: retval = {}!", ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval = {} after {} tries.", apiName, ctxId, rv, cnt); + } + } + + Interface1 ipIf = swIf.getAugmentation(Interface1.class); + LOG.info("VPPCFG-INFO: ipIf = {}", ipIf); + if (ipIf != null) { + Ipv4 v4 = ipIf.getIpv4(); + if (v4 != null) { + LOG.info("VPPCFG-INFO: v4 = {}", v4); + + for (Address v4Addr : v4.getAddress()) { + Subnet subnet = v4Addr.getSubnet(); + + if (subnet instanceof PrefixLength) { + Short plen = ((PrefixLength)subnet).getPrefixLength(); + byte [] addr = ipv4AddressNoZoneToArray(v4Addr.getIp()); + + if ((plen > 0) && (addr != null)) { + apiName = "api.swInterfaceAddDelAddress"; + ctxId = + api.swInterfaceAddDelAddress((int)swIfIndex, + (byte)1 /* isAdd */, + (byte)0 /* isIpv6 */, + (byte)0 /* delAll */, + plen.byteValue(), addr); + LOG.info("VPPCFG-INFO: {}({}/{}) to {} ({}): {}()" + + " returned ctxId = {}", apiName, addr, + plen, swIfName, swIfIndex, ctxId); + cnt = 0; + rv = -77; + while (rv == -77) { + rv = api.getRetval(ctxId, 1 /* release */); + cnt++; + } + if (rv < 0) { + LOG.warn("VPPCFG-WARNING: {}() ctxId = {} " + + "failed: retval = {}!", apiName, + ctxId, rv); + /* DAW-FIXME: throw exception on failure? */ + } else { + LOG.info("VPPCFG-INFO: {}() ctxId = {} retval" + + " = {} after {} tries.", apiName, + ctxId, rv, cnt); + } + } else { + LOG.warn("VPPCFG-WARNING: Malformed ipv4 address ({}/{}) " + + "specified for {} ({}): ignoring config!", + addr, plen, swIfName, swIfIndex); + } + } else if (subnet instanceof Netmask) { + LOG.warn("VPPCFG-WARNING: Unsupported ipv4 address subnet type 'Netmask' " + + "specified for {} ({}): ignoring config!", + swIfName, swIfIndex); + } else { + LOG.error("VPPCFG-ERROR: Unknown ipv4 address subnet type " + + "specified for {} ({}): ignoring config!", + swIfName, swIfIndex); + } + } + } + + Ipv6 v6 = ipIf.getIpv6(); + if (v6 != null) { + LOG.info("VPPCFG-INFO: v6 = {}", v6); + + // DAW-FIXME: Add Ipv6 address support. + LOG.warn("VPPCFG-WARNING: Ipv6 address support TBD: ignoring config!"); + } + } + } + + private void createOrUpdateInterface(DataObjectModification<Interface> changeDiff) { + if (changeDiff.getDataBefore() == null) { + // create + vppSetInterface(changeDiff.getDataAfter(), + DataChangeType.CREATE, null); + } else { + // update + vppSetInterface(changeDiff.getDataAfter(), + DataChangeType.UPDATE, + changeDiff.getDataBefore()); + } + } + + private void deleteInterface(DataObjectModification<Interface> changeDiff) { + Interface swIf = changeDiff.getDataBefore(); + LOG.info("VPPCFG-INFO: <swIf>"); + LOG.info("VPPCFG-INFO: Name: " + swIf.getName()); + LOG.info("VPPCFG-INFO: Desc: " + swIf.getDescription()); + LOG.info("VPPCFG-INFO: Type: " + swIf.getType().getSimpleName()); + LOG.info("VPPCFG-INFO: </swIf>"); + + if (swIf.getType().isAssignableFrom(EthernetCsmacd.class)) { + LOG.error("VPPCFG-ERROR: {} Interface {} cannot be deleted!", + swIf.getType().getSimpleName(), + swIf.getName()); + + /* DAW-FIXME: Add additional interface types here. + * + * } else if (swIf.getType().isAssignableFrom(*.class)) { + */ + + } else { + LOG.error("VPPCFG-ERROR: Unsupported interface type ({}) : " + + "{} cannot be deleted!", + swIf.getType().getSimpleName(), + swIf.getName()); + } + } + + @Override + public void close() throws Exception { + registration.close(); + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppPollOperDataImpl.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppPollOperDataImpl.java new file mode 100644 index 000000000..71d4bea4b --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppPollOperDataImpl.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.impl; + +import java.util.HashMap; +import java.util.concurrent.Future; + +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +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.TransactionCommitFailedException; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.V3poService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppPollOperDataOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppPollOperDataOutputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.openvpp.vppjapi.vppBridgeDomainDetails; +import org.openvpp.vppjapi.vppBridgeDomainInterfaceDetails; +import org.openvpp.vppjapi.vppL2Fib; +import org.openvpp.vppjapi.vppVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VppPollOperDataImpl implements V3poService { + private static final Logger LOG = LoggerFactory.getLogger(VppPollOperDataImpl.class); + private vppVersion version; + private DataBroker db; + private int[] bdIds; + private HashMap<Integer, vppL2Fib[]> l2fibByBdId = new HashMap<Integer, vppL2Fib[]>(); + + /** + * TODO-ADD-JAVADOC. + */ + public VppPollOperDataImpl(DataBroker dataBroker) { + db = dataBroker; + } + + /** + * TODO-ADD-JAVADOC. + */ + public DataBroker getDataBroker() { + return db; + } + + /** + * TODO-ADD-JAVADOC. + * Update operational data and return string of space separated + * interfaces names + */ + public String updateOperational() { + V3poApiRequest api = new V3poApiRequest(this); + version = api.getVppVersion(); + + bdIds = api.bridgeDomainDump(-1); + + // TODO: we don't need to cache BDs now that we got rid of callbacks + l2fibByBdId.clear(); + if (bdIds != null) { + for (int idx = 0; idx < bdIds.length; idx++) { + l2fibByBdId.put(bdIds[idx], api.l2FibTableDump(bdIds[idx])); + } + } + api.swInterfaceDumpAll(); + + // build vpp-state + VppStateCustomBuilder stateBuilder = new VppStateCustomBuilder(); + + // bridge domains + for (int i = 0; i < bdIds.length; i++) { + vppBridgeDomainDetails bd = api.getBridgeDomainDetails(bdIds[i]); + VppStateBridgeDomainBuilder bdBuilder = + new VppStateBridgeDomainBuilder( + bd.name, bd.flood, bd.uuFlood, + bd.arpTerm, bd.forward, bd.learn); + + for (int ifIdx = 0; ifIdx < bd.interfaces.length; ifIdx++) { + vppBridgeDomainInterfaceDetails bdIf = bd.interfaces[ifIdx]; + bdBuilder.addInterface(bdIf.interfaceName, + bd.bviInterfaceName == bdIf.interfaceName, + bdIf.splitHorizonGroup); + } + + vppL2Fib[] bdFibs = l2fibByBdId.get(bdIds[i]); + + for (int fibIdx = 0; fibIdx < bdFibs.length; fibIdx++) { + vppL2Fib fib = bdFibs[fibIdx]; + bdBuilder.addL2Fib(fib.filter, fib.bridgedVirtualInterface, + fib.outgoingInterface, fib.physAddress, + fib.staticConfig); + } + + stateBuilder.addBridgeDomain(bdBuilder.build()); + } + + stateBuilder.setVersion(version); + + // write to oper + writeVppState(getVppStateIid(), stateBuilder.build()); + + return api.ifNames; + } + + @Override + public Future<RpcResult<VppPollOperDataOutput>> vppPollOperData() { + String ifNames = updateOperational(); + + + VppPollOperDataOutput output = new VppPollOperDataOutputBuilder() + .setStatus(new Long(1)).build(); + + return RpcResultBuilder.success(output).buildFuture(); + } + + private InstanceIdentifier<VppState> getVppStateIid() { + return (InstanceIdentifier.create(VppState.class)); + } + + private void writeVppState(InstanceIdentifier<VppState> iid, VppState vppState) { + WriteTransaction transaction = db.newWriteOnlyTransaction(); + + //LOG.info("VPPOPER-INFO: Writing vpp-state to oper DataStore."); + transaction.put(LogicalDatastoreType.OPERATIONAL, iid, vppState); + + CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit(); + Futures.addCallback(future, new LoggingFuturesCallBack<Void>( + "VPPOPER-WARNING: Failed to write vpp-state to oper datastore", LOG)); + } +} + diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppStateBridgeDomainBuilder.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppStateBridgeDomainBuilder.java new file mode 100644 index 000000000..d547342bd --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppStateBridgeDomainBuilder.java @@ -0,0 +1,102 @@ +/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomainBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.InterfaceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.L2Fib;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.L2Fib.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.L2FibBuilder;
+
+public class VppStateBridgeDomainBuilder {
+ private BridgeDomainBuilder bdStateBuilder = new BridgeDomainBuilder();
+ private List<Interface> bdIfaces = new ArrayList<Interface>();
+ private List<L2Fib> bdL2Fibs = new ArrayList<L2Fib>();
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public VppStateBridgeDomainBuilder(String bdName, boolean flood,
+ boolean unknownUnicastFlood,
+ boolean arpTermination,
+ boolean forward, boolean learn) {
+ bdStateBuilder
+ .setName(bdName)
+ .setFlood(flood)
+ .setUnknownUnicastFlood(unknownUnicastFlood)
+ .setArpTermination(arpTermination)
+ .setForward(forward)
+ .setLearn(learn);
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void addInterface(String interfaceName, boolean bvi,
+ short splitHorizonGroup) {
+ InterfaceBuilder ifBuilder = new InterfaceBuilder();
+ ifBuilder
+ .setName(interfaceName)
+ .setBridgedVirtualInterface(bvi)
+ .setSplitHorizonGroup(splitHorizonGroup);
+
+ bdIfaces.add(ifBuilder.build());
+ }
+
+ private static String getMacAddress(byte[] mac) {
+ StringBuilder sb = new StringBuilder(18);
+ for (byte b : mac) {
+ if (sb.length() > 0) {
+ sb.append(':');
+ }
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void addL2Fib(boolean filter, boolean bvi,
+ String outgoingIfaceName, byte[] physAddress,
+ boolean isStatic) {
+ L2FibBuilder l2fibBuilder = new L2FibBuilder();
+ l2fibBuilder
+ .setAction((filter ? Action.Filter : Action.Forward))
+ .setBridgedVirtualInterface(bvi)
+ .setOutgoingInterface(outgoingIfaceName)
+ .setPhysAddress(new PhysAddress(getMacAddress(physAddress)))
+ .setStaticConfig(isStatic);
+
+ bdL2Fibs.add(l2fibBuilder.build());
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public BridgeDomain build() {
+ bdStateBuilder.setInterface(bdIfaces);
+ bdStateBuilder.setL2Fib(bdL2Fibs);
+ return bdStateBuilder.build();
+ }
+}
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppStateCustomBuilder.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppStateCustomBuilder.java new file mode 100644 index 000000000..86e44ec0b --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/VppStateCustomBuilder.java @@ -0,0 +1,73 @@ +/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppStateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.BridgeDomainsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.VersionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomain;
+import org.openvpp.vppjapi.vppVersion;
+
+public class VppStateCustomBuilder {
+ VppStateBuilder stateBuilder = new VppStateBuilder();
+
+ List<BridgeDomain> bridgeDomains = new ArrayList<BridgeDomain>();
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void setVersion(String name, String branch, String buildDate,
+ String buildDir) {
+ stateBuilder.setVersion(
+ new VersionBuilder()
+ .setBranch(branch)
+ .setBuildDate(buildDate)
+ .setBuildDirectory(buildDir)
+ .setName(name)
+ .build());
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void setVersion(vppVersion vppVer) {
+ setVersion(vppVer.programName, vppVer.gitBranch,
+ vppVer.buildDate, vppVer.buildDirectory);
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public void addBridgeDomain(BridgeDomain bd) {
+ bridgeDomains.add(bd);
+ }
+
+ /**
+ * TODO-ADD-JAVADOC.
+ */
+ public VppState build() {
+ stateBuilder.setBridgeDomains(
+ new BridgeDomainsBuilder()
+ .setBridgeDomain(bridgeDomains)
+ .build());
+ return stateBuilder.build();
+ }
+}
diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java new file mode 100644 index 000000000..5a5fd1147 --- /dev/null +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; + +import io.fd.honeycomb.v3po.impl.V3poProvider; + +public class V3poModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModule { + public V3poModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public V3poModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.V3poModule 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() { + V3poProvider provider = new V3poProvider(); + getBrokerDependency().registerProvider(provider); + return provider; + } + +} diff --git a/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java new file mode 100644 index 000000000..5c942861d --- /dev/null +++ b/v3po/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* +* Generated file +* +* Generated from: yang module name: v3po yang module local name: v3po +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Fri Jan 02 13:49:24 CST 2015 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; +public class V3poModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModuleFactory { + +} diff --git a/v3po/impl/src/main/yang/v3po-impl.yang b/v3po/impl/src/main/yang/v3po-impl.yang new file mode 100644 index 000000000..7a6fd4277 --- /dev/null +++ b/v3po/impl/src/main/yang/v3po-impl.yang @@ -0,0 +1,35 @@ +module v3po-impl { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:v3po:impl"; + prefix "v3po-impl"; + + import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;} + + description + "Service definition for v3po project"; + + revision "2014-12-10" { + description + "Initial revision"; + } + + identity v3po { + base config:module-type; + config:java-name-prefix V3po; + } + + augment "/config:modules/config:module/config:configuration" { + case v3po { + when "/config:modules/config:module/config:type = 'v3po'"; + container broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity md-sal-binding:binding-broker-osgi-registry; + } + } + } + } + } +} diff --git a/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java b/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java new file mode 100644 index 000000000..5b9a67458 --- /dev/null +++ b/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; + +import org.junit.Test; + +public class V3poModuleFactoryTest { + @Test + public void testFactoryConstructor() { + // ensure no exceptions on construction + new V3poModuleFactory(); + } +} diff --git a/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java b/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java new file mode 100644 index 000000000..99872549a --- /dev/null +++ b/v3po/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210; + +import org.junit.Test; +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.JmxAttribute; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import io.fd.honeycomb.v3po.impl.V3poProvider; + +import javax.management.ObjectName; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class V3poModuleTest { + @Test + public void testCustomValidation() { + V3poModule module = new V3poModule(mock(ModuleIdentifier.class), mock(DependencyResolver.class)); + + // ensure no exceptions on validation + // currently this method is empty + module.customValidation(); + } + + @Test + public void testCreateInstance() throws Exception { + // configure mocks + DependencyResolver dependencyResolver = mock(DependencyResolver.class); + BindingAwareBroker broker = mock(BindingAwareBroker.class); + when(dependencyResolver.resolveInstance(eq(BindingAwareBroker.class), any(ObjectName.class), any(JmxAttribute.class))).thenReturn(broker); + + // create instance of module with injected mocks + V3poModule module = new V3poModule(mock(ModuleIdentifier.class), dependencyResolver); + + // getInstance calls resolveInstance to get the broker dependency and then calls createInstance + AutoCloseable closeable = module.getInstance(); + + // verify that the module registered the returned provider with the broker + verify(broker).registerProvider((V3poProvider)closeable); + + // ensure no exceptions on close + closeable.close(); + } +} |