/* * Copyright (c) 2015 Huawei, Inc and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.nic.nemo.renderer; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; import org.opendaylight.nemo.intent.IntentResolverUtils; import org.opendaylight.nic.nemo.rpc.NemoDelete; import org.opendaylight.nic.nemo.rpc.NemoRpc; import org.opendaylight.nic.nemo.rpc.NemoUpdate; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.Intents; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intents.Intent; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intents.IntentKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.common.rev151010.UserId; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.BeginTransactionInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.BeginTransactionInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.BeginTransactionOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.CommonRpcResult; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.CommonRpcResult.ResultCode; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.EndTransactionInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.EndTransactionInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.NemoIntentService; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.Users; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.users.User; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.users.UserKey; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; /** * * @author gwu * */ public class NEMORenderer implements AutoCloseable, DataChangeListener { private static final Logger LOG = LoggerFactory.getLogger(NEMORenderer.class); public static final String NIC_PREFIX = "nic-"; public static final InstanceIdentifier<Intent> INTENT_IID = InstanceIdentifier.builder(Intents.class) .child(Intent.class).build(); private final DataBroker dataBroker; private final RpcProviderRegistry rpcProviderRegistry; private final NemoIntentService nemoEngine; private ListenerRegistration<DataChangeListener> listenerRegistration; public NEMORenderer(DataBroker dataBroker0, RpcProviderRegistry rpcProviderRegistry0) { this.dataBroker = dataBroker0; this.rpcProviderRegistry = rpcProviderRegistry0; this.nemoEngine = rpcProviderRegistry.getRpcService(NemoIntentService.class); } public void init() { LOG.info("Initializing NEMORenderer"); listenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, INTENT_IID, this, DataChangeScope.SUBTREE); } @Override public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) { // TODO: replace this when operational data initialization is finalized IntentResolverUtils.copyPhysicalNetworkConfigToOperational(dataBroker); create(changes.getCreatedData()); update(changes.getUpdatedData()); delete(changes); } private void create(Map<InstanceIdentifier<?>, DataObject> changes) { for (Entry<InstanceIdentifier<?>, DataObject> created : changes.entrySet()) { if (created.getValue() instanceof Intent) { LOG.info("Created Intent {}.", created); try { createOrUpdateIntent((Intent) created.getValue()); } catch (InterruptedException | ExecutionException e) { // the call is synchronous, so this should never occur LOG.error("Unexpected exception", e); } } } } private void update(Map<InstanceIdentifier<?>, DataObject> changes) { for (Entry<InstanceIdentifier<?>, DataObject> updated : changes.entrySet()) { if (updated.getValue() instanceof Intent) { LOG.info("Updated Intent {}.", updated); try { createOrUpdateIntent((Intent) updated.getValue()); } catch (InterruptedException | ExecutionException e) { // the call is synchronous, so this should never occur LOG.error("Unexpected exception", e); } } } } private void delete(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) { for (InstanceIdentifier<?> deleted : changes.getRemovedPaths()) { if (deleted.getTargetType().equals(Intent.class)) { IntentKey intentKey = deleted.firstKeyOf(Intent.class); LOG.info("Deleting IntentKey {}.", intentKey); try { deleteIntent(intentKey); } catch (InterruptedException | ExecutionException e) { // the call is synchronous, so this should never occur LOG.error("Unexpected exception", e); } } else { LOG.trace("Skipping NIC Intent {} deletion", deleted); } } } private boolean executeNemoRpc(UserId userId, NemoRpc nemoRpc) throws InterruptedException, ExecutionException { if (!nemoRpc.isInputValid()) { LOG.info("NemoRpc {} input is invalid", nemoRpc.getClass().getSimpleName()); return false; } final Optional<User> userOpt = readUser(userId); if (!userOpt.isPresent()) { LOG.info("UserId {} not found", userId); return false; } final User user = userOpt.get(); // run all three operations even if there were already failures boolean r1 = beginTransaction(userId); RpcResult<? extends CommonRpcResult> rpcResult = null; try { rpcResult = nemoRpc.apply(nemoEngine, user); } catch (Exception e) { LOG.warn("NemoRpc failed: ", e); } boolean r2 = isSuccessful(rpcResult); if (!r2) { LOG.warn("NemoRpc {} failed: {}", nemoRpc.getClass().getSimpleName(), rpcResult); } boolean r3 = endTransaction(userId); return r1 && r2 && r3; } /** * * @param intent * @return true if the intent create/update was successful * @throws InterruptedException * @throws ExecutionException */ @VisibleForTesting boolean createOrUpdateIntent(Intent intent) throws InterruptedException, ExecutionException { return executeNemoRpc(getUserId(intent.getKey()), new NemoUpdate(intent)); } /** * * @param intent * @return true if the intent create/update was successful * @throws InterruptedException * @throws ExecutionException */ @VisibleForTesting private boolean deleteIntent(IntentKey intentKey) throws InterruptedException, ExecutionException { return executeNemoRpc(getUserId(intentKey), new NemoDelete()); } private static boolean isSuccessful(RpcResult<? extends CommonRpcResult> rpcResult) { return rpcResult != null && rpcResult.isSuccessful() && rpcResult.getResult().getResultCode() == ResultCode.Ok; } private boolean beginTransaction(UserId userId) throws InterruptedException, ExecutionException { BeginTransactionInput input = new BeginTransactionInputBuilder().setUserId(userId).build(); RpcResult<BeginTransactionOutput> r1 = nemoEngine.beginTransaction(input).get(); boolean success = isSuccessful(r1); if (!success) { LOG.warn("NemoEngine beginTransaction failed: " + r1.getResult()); } return success; } private boolean endTransaction(UserId userId) throws InterruptedException, ExecutionException { EndTransactionInput input = new EndTransactionInputBuilder().setUserId(userId).build(); RpcResult<? extends CommonRpcResult> r3 = nemoEngine.endTransaction(input).get(); boolean success = isSuccessful(r3); if (!success) { LOG.warn("NemoEngine endTransaction failed: " + r3.getResult()); } return success; } private Optional<User> readUser(UserId userId) throws InterruptedException, ExecutionException { try (ReadOnlyTransaction txn = dataBroker.newReadOnlyTransaction()) { InstanceIdentifier<User> userPath = InstanceIdentifier.builder(Users.class) .child(User.class, new UserKey(userId)).build(); return txn.read(LogicalDatastoreType.CONFIGURATION, userPath).get(); } } /** * * @param intentKey * @return the user that created this intent */ private static UserId getUserId(IntentKey intentKey) { // for this release, assume userId to be the same as the NIC intent ID return new UserId(intentKey.getId().getValue()); } @Override public void close() throws Exception { if (listenerRegistration != null) { listenerRegistration.close(); } } }