org.opendaylight.vtn.manager.internal.vnode.MacMapActivation.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.vtn.manager.internal.vnode.MacMapActivation.java

Source

/*
 * Copyright (c) 2015 NEC Corporation. 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.vtn.manager.internal.vnode;

import static org.opendaylight.vtn.manager.internal.vnode.MappingRegistry.LOG;

import com.google.common.base.Optional;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import org.opendaylight.vtn.manager.VTNException;

import org.opendaylight.vtn.manager.internal.TxContext;
import org.opendaylight.vtn.manager.internal.flow.remove.EdgeHostFlowRemover;
import org.opendaylight.vtn.manager.internal.flow.remove.FlowRemoverQueue;
import org.opendaylight.vtn.manager.internal.util.DataStoreUtils;
import org.opendaylight.vtn.manager.internal.util.inventory.MacVlan;
import org.opendaylight.vtn.manager.internal.util.inventory.PortVlan;
import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
import org.opendaylight.vtn.manager.internal.util.vnode.MacMapGoneException;
import org.opendaylight.vtn.manager.internal.util.vnode.MacMapIdentifier;
import org.opendaylight.vtn.manager.internal.util.vnode.MacMapPortBusyException;
import org.opendaylight.vtn.manager.internal.util.vnode.VBridgeIdentifier;
import org.opendaylight.vtn.manager.internal.util.vnode.VNodeIdentifier;
import org.opendaylight.vtn.manager.internal.util.vnode.VTNMacMapStatus;
import org.opendaylight.vtn.manager.internal.util.vnode.VlanMapIdentifier;

import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;

import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;

import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.mapping.rev151001.vtn.mappings.PortMapping;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.impl.mapping.rev151001.vtn.mappings.PortMappingBuilder;

/**
 * {@code MacMapActivation} describes a runtime context for activating the
 * MAC mapping.
 */
public final class MacMapActivation {
    /**
     * The identifier for the target MAC mapping.
     */
    private final MacMapIdentifier targetId;

    /**
     * The L2 host to be mapped by the MAC mapping.
     */
    private final MacVlan targetHost;

    /**
     * The physical switch port where the target host is detected.
     */
    private final SalPort targetPort;

    /**
     * A reference to the VLAN mapping which maps the VLAN specified by
     * {@link #reservedNetwork}.
     */
    private VlanMapIdentifier vlanMap;

    /**
     * A {@link PortVlan} instance which represents the VLAN reserved by the
     * MAC mapping.
     */
    private PortVlan reservedNetwork;

    /**
     * Construct a new instance.
     *
     * @param ident  The identifier for the target MAC mapping.
     * @param mv     A {@link MacVlan} instance which represents L2 host
     *               information.
     * @param sport  A {@link SalPort} instance corresponding to a switch port
     *               where the specified host is detected.
     */
    public MacMapActivation(MacMapIdentifier ident, MacVlan mv, SalPort sport) {
        targetId = ident;
        targetHost = mv;
        targetPort = sport;
    }

    /**
     * Activate the specified host in the MAC mapping.
     *
     * @param ctx    MD-SAL datastore transaction context.
     * @return  {@code true} is returned if the specified MAC mapping was
     *          activated. In other words, {@code true} is returned if the
     *          specified host was activated, and it is the only active host
     *          in the MAC mapping. Otherwise {@code false} is returned.
     * @throws MacMapGoneException
     *    The specified host is no longer mapped by the target MAC mapping.
     * @throws MacMapPortBusyException
     *    The specified VLAN network on a switch port is reserved by
     *    another virtual mapping.
     * @throws org.opendaylight.vtn.manager.internal.util.vnode.MacMapDuplicateException
     *    The same MAC address as {@code mv} is already mapped to the same
     *    vBridge.
     * @throws VTNException  A fatal error occurred.
     */
    public boolean activate(TxContext ctx) throws VTNException {
        // Ensure that the specified host is mapped by the specified
        // MAC mapping.
        ReadWriteTransaction tx = ctx.getReadWriteTransaction();
        checkMacMapping(tx);

        // Reserve the VLAN on the specified switch port for the MAC mapping.
        int vid = targetHost.getVlanId();
        PortVlan pv = new PortVlan(targetPort, vid);
        boolean reserved = reservePort(tx, pv);

        // Activate the MAC mapping.
        boolean activated;
        Triple<Boolean, SalPort, PortVlan> result = activate(ctx, pv, reserved);
        if (result == null) {
            // The specified host is already activated.
            activated = false;
        } else {
            activated = result.getLeft().booleanValue();
            cleanUp(ctx, result.getMiddle());
        }

        return activated;
    }

    /**
     * Ensure that the specified host is mapped by the specified MAC mapping.
     *
     * @param tx  A transaction for the MD-SAL datastore.
     * @throws MacMapGoneException
     *    The specified host is no longer mapped by the target MAC mapping.
     * @throws VTNException  A fatal error occurred.
     */
    private void checkMacMapping(ReadWriteTransaction tx) throws VTNException {
        String id = targetId.toString();
        Pair<MacVlan, String> allowed = MappingRegistry.getMacMapAllowed(tx, targetHost);
        if (allowed == null || !id.equals(allowed.getRight())) {
            throw new MacMapGoneException(targetHost, targetId);
        }
    }

    /**
     * Reserve the specified VLAN on a switch port for the specified
     * MAC mapping.
     *
     * @param tx  A MD-SAL datastore transaction.
     * @param pv  A {@link PortVlan} instance that indicates the VLAN to be
     *            reserved.
     * @return  {@code true} if the given port has been reserved successfully.
     *          {@code false} if the given port is already reserved.
     * @throws MacMapPortBusyException
     *    The specified VLAN network on a switch port is reserved by another
     *    virtual mapping.
     * @throws VTNException  An error occurred.
     */
    private boolean reservePort(ReadWriteTransaction tx, PortVlan pv) throws VTNException {
        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
        InstanceIdentifier<PortMapping> path = MappingRegistry.getPath(pv);
        Optional<PortMapping> opt = DataStoreUtils.read(tx, oper, path);
        boolean present = opt.isPresent();
        if (present) {
            String map = opt.get().getMapping();
            if (!targetId.toString().equals(map)) {
                // The specified port is reserved by a port mapping.
                VNodeIdentifier<?> ident = VNodeIdentifier.create(map);
                throw new MacMapPortBusyException(targetHost, targetId, ident);
            }
        } else {
            // Reserve the specified port for the MAC mapping.
            PortMapping pmap = new PortMappingBuilder().setIdentifier(pv.toString()).setMapping(targetId.toString())
                    .build();
            tx.put(oper, path, pmap, true);
        }

        return !present;
    }

    /**
     * Activate the target MAC mapping.
     *
     * @param ctx       MD-SAL datastore transaction context.
     * @param pv        A {@link PortVlan} instance that indicates the VLAN to
     *                  be reserved.
     * @param reserved  {@code true} means that the target VLAN on the switch
     *                  port is reserved.
     * @return
     *   <p>
     *     A {@link Triple} instance is returned if the MAC mapping was
     *     successfully activated.
     *   </p>
     *   <ul>
     *     <li>
     *       A boolean value that indicates the result is set to the left.
     *       {@code true} means that the target MAC mapping has been activated.
     *       {@code false} means that the target MAC mapping is already
     *       activated.
     *     </li>
     *     <li>
     *       A {@link SalPort} instance that specifies the physical switch port
     *       previously associated with the L2 host in the MAC mapping is set
     *       to the middle.
     *     </li>
     *     <li>
     *       A {@link PortVlan} instance that specifies the VLAN to be released
     *       is set to the right.
     *     </li>
     *   </ul>
     *   <p>
     *     {@code null} is returned if the MAC mapping for the specified
     *     host is already activated.
     *   </p>
     * @throws org.opendaylight.vtn.manager.internal.util.vnode.MacMapDuplicateException
     *    The specified MAC address is already mapped by this MAC mapping.
     * @throws VTNException  A fatal error occurred.
     */
    private Triple<Boolean, SalPort, PortVlan> activate(TxContext ctx, PortVlan pv, boolean reserved)
            throws VTNException {
        MacMapStatusReader reader = ctx.getSpecific(MacMapStatusReader.class);
        VTNMacMapStatus vmst = reader.get(targetId);
        Triple<Boolean, SalPort, PortVlan> result = vmst.activate(targetId, targetHost, targetPort);
        if (result != null) {
            PortVlan released = result.getRight();
            if (released != null) {
                // Release the switch port which is no longer used by the
                // target MAC mapping.
                MappingRegistry.releasePort(ctx, released, targetId);
            }
            if (reserved) {
                ReadWriteTransaction tx = ctx.getReadWriteTransaction();
                VlanMapIdentifier vmapId = MappingRegistry.getVlanMapping(tx, pv.getPort(), pv.getVlanId());
                if (vmapId != null) {
                    // Need to purge network caches superseded by the
                    // MAC mapping.
                    vlanMap = vmapId;
                    reservedNetwork = pv;
                }
            }
        } else if (reserved) {
            // This should never happen.
            String msg = "Port is reserved by MAC mapping unexpectedly: " + "map=" + targetId + ", host="
                    + targetHost + ", port=" + targetPort;
            throw new VTNException(msg);
        }

        return result;
    }

    /**
     * Clean up the transaction for activating the MAC mapping.
     *
     * @param ctx      MD-SAL datastore transaction context.
     * @param oldPort  A {@link SalPort} instance that was previously
     *                 associated with the target host in the MAC mapping.
     * @throws VTNException  A fatal error occurred.
     */
    private void cleanUp(TxContext ctx, SalPort oldPort) throws VTNException {
        if (vlanMap != null) {
            // Purge obsolete network caches originated by the VLAN mapping.
            PortMapCleaner cleaner = new PortMapCleaner();
            cleaner.add(reservedNetwork);
            cleaner.purge(ctx, vlanMap.getTenantNameString());
        }

        if (oldPort == null) {
            if (LOG.isTraceEnabled()) {
                LOG.trace("{}: MAC mapping has been activated: host={}, " + "port={}", targetId, targetHost,
                        targetPort);
            }
        } else {
            if (LOG.isTraceEnabled()) {
                LOG.trace("{}: MAC mapped host has been moved: host={}, " + "from={}, to={}", targetId, targetHost,
                        oldPort, targetPort);
            }

            // Remove MAC addresses registered by old MAC mapping.
            VBridgeIdentifier vbrId = targetId.getBridgeIdentifier();
            MacEntryRemover.remove(ctx, vbrId, targetHost.getMacAddress());

            // Remove flow entries registered by old MAC mapping.
            String tname = targetId.getTenantNameString();
            EdgeHostFlowRemover remover = new EdgeHostFlowRemover(tname, targetHost, targetPort);
            ctx.getSpecific(FlowRemoverQueue.class).enqueue(remover);
        }
    }
}