blob: 7d427ba5d7464164e5db6b40f6f64c8d3914a42e [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Laboratory
*
* 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.onosproject.l3vpn.netl3vpn.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.util.AbstractAccumulator;
import org.onlab.util.Accumulator;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.LeadershipEvent;
import org.onosproject.cluster.LeadershipEventListener;
import org.onosproject.cluster.LeadershipService;
import org.onosproject.cluster.NodeId;
import org.onosproject.config.DynamicConfigEvent;
import org.onosproject.config.DynamicConfigListener;
import org.onosproject.config.DynamicConfigService;
import org.onosproject.config.FailedException;
import org.onosproject.config.Filter;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.core.IdGenerator;
import org.onosproject.l3vpn.netl3vpn.AccessInfo;
import org.onosproject.l3vpn.netl3vpn.BgpDriverInfo;
import org.onosproject.l3vpn.netl3vpn.BgpInfo;
import org.onosproject.l3vpn.netl3vpn.DeviceInfo;
import org.onosproject.l3vpn.netl3vpn.FullMeshVpnConfig;
import org.onosproject.l3vpn.netl3vpn.HubSpokeVpnConfig;
import org.onosproject.l3vpn.netl3vpn.InterfaceInfo;
import org.onosproject.l3vpn.netl3vpn.NetL3VpnException;
import org.onosproject.l3vpn.netl3vpn.NetL3VpnStore;
import org.onosproject.l3vpn.netl3vpn.VpnConfig;
import org.onosproject.l3vpn.netl3vpn.VpnInstance;
import org.onosproject.l3vpn.netl3vpn.VpnSiteRole;
import org.onosproject.l3vpn.netl3vpn.VpnType;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverService;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev20140508.ietfinterfaces.devices.device.Interfaces;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.DefaultL3VpnSvc;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.L3VpnSvc;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.accessvpnpolicy.VpnAttachment;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.accessvpnpolicy.vpnattachment.AttachmentFlavor;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.accessvpnpolicy.vpnattachment.attachmentflavor.DefaultVpnId;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.DefaultSites;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.Sites;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.VpnServices;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.sites.Site;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.sites.site.SiteNetworkAccesses;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.sites.site.sitenetworkaccesses.SiteNetworkAccess;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.l3vpnsvc.vpnservices.VpnSvc;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siteattachmentbearer.Bearer;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siteattachmentbearer.DefaultBearer;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siteattachmentbearer.bearer.DefaultRequestedType;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siteattachmentbearer.bearer.RequestedType;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siteattachmentipconnection.IpConnection;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siterouting.RoutingProtocols;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.l3vpn.svc.rev20160730.ietfl3vpnsvc.siterouting.routingprotocols.RoutingProtocol;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.instance.rev20160623.ietfnetworkinstance.devices.device.NetworkInstances;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.l3vpn.svc.ext.rev20160730.l3vpnsvcext.l3vpnsvc.sites.site.sitenetworkaccesses.sitenetworkaccess.bearer.DefaultAugmentedL3VpnBearer;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.l3vpn.svc.ext.rev20160730.l3vpnsvcext.l3vpnsvc.sites.site.sitenetworkaccesses.sitenetworkaccess.bearer.requestedtype.DefaultAugmentedL3VpnRequestedType;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.l3vpn.svc.ext.rev20160730.l3vpnsvcext.requestedtypegrouping.requestedtypeprofile.RequestedTypeChoice;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.l3vpn.svc.ext.rev20160730.l3vpnsvcext.requestedtypegrouping.requestedtypeprofile.requestedtypechoice.DefaultDot1Qcase;
import org.onosproject.yang.gen.v1.urn.ietf.params.xml.ns.yang.l3vpn.svc.ext.rev20160730.l3vpnsvcext.requestedtypegrouping.requestedtypeprofile.requestedtypechoice.DefaultPhysicalCase;
import org.onosproject.yang.model.DataNode;
import org.onosproject.yang.model.DefaultModelObjectData;
import org.onosproject.yang.model.ModelConverter;
import org.onosproject.yang.model.ModelObject;
import org.onosproject.yang.model.ModelObjectData;
import org.onosproject.yang.model.ModelObjectId;
import org.onosproject.yang.model.NodeKey;
import org.onosproject.yang.model.ResourceData;
import org.onosproject.yang.model.ResourceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.config.DynamicConfigEvent.Type.NODE_ADDED;
import static org.onosproject.config.DynamicConfigEvent.Type.NODE_DELETED;
import static org.onosproject.l3vpn.netl3vpn.VpnType.HUB;
import static org.onosproject.l3vpn.netl3vpn.impl.BgpConstructionUtil.createBgpInfo;
import static org.onosproject.l3vpn.netl3vpn.impl.InsConstructionUtil.createInstance;
import static org.onosproject.l3vpn.netl3vpn.impl.InsConstructionUtil.deleteInstance;
import static org.onosproject.l3vpn.netl3vpn.impl.IntConstructionUtil.createInterface;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.BEARER_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.CONS_HUNDRED;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.DEVICE_INFO_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.EVENT_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.ID_LIMIT;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.ID_LIMIT_EXCEEDED;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.INT_INFO_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.IP;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.IP_INT_INFO_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.MAX_BATCH_MS;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.MAX_EVENTS;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.MAX_IDLE_MS;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.PORT_NAME;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.SITE_ROLE_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.SITE_VPN_MISMATCH;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.TIMER;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.UNKNOWN_EVENT;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.VPN_ATTACHMENT_NULL;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.VPN_POLICY_NOT_SUPPORTED;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.VPN_TYPE_UNSUPPORTED;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getBgpCreateConfigObj;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getIntCreateModObj;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getIntNotAvailable;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getMgmtIpUnAvailErr;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getModIdForL3VpnSvc;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getModIdForSites;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getResourceData;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getRole;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getVpnBgpDelModObj;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getVpnCreateModObj;
import static org.onosproject.l3vpn.netl3vpn.impl.NetL3VpnUtil.getVpnDelModObj;
/**
* The IETF net l3vpn manager implementation.
*/
@Component(immediate = true)
public class NetL3VpnManager {
private static final String APP_ID = "org.onosproject.app.l3vpn";
private static final String L3_VPN_ID_TOPIC = "l3vpn-id";
private final Logger log = LoggerFactory.getLogger(getClass());
private final DynamicConfigListener configListener =
new InternalConfigListener();
private final Accumulator<DynamicConfigEvent> accumulator =
new InternalEventAccumulator();
private final InternalLeadershipListener leadershipEventListener =
new InternalLeadershipListener();
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ModelConverter modelConverter;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DynamicConfigService configService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetL3VpnStore l3VpnStore;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected LeadershipService leadershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
protected IdGenerator l3VpnIdGen;
private NodeId localNodeId;
private ApplicationId appId;
private ResourceId id;
private ResourceId module;
private ResourceId sites;
private boolean isElectedLeader;
@Activate
protected void activate() {
appId = coreService.registerApplication(APP_ID);
l3VpnIdGen = coreService.getIdGenerator(L3_VPN_ID_TOPIC);
localNodeId = clusterService.getLocalNode().id();
leadershipService.addListener(leadershipEventListener);
leadershipService.runForLeadership(appId.name());
getResourceId();
configService.addListener(configListener);
log.info("Started");
}
@Deactivate
protected void deactivate() {
configService.removeListener(configListener);
leadershipService.withdraw(appId.name());
leadershipService.removeListener(leadershipEventListener);
log.info("Stopped");
}
/**
* Returns id as string. If the id is not in the freed list a new id is
* generated else the id from the freed list is used.
*
* @return id
*/
private String getIdFromGen() {
Long value;
Iterable<Long> freeIds = l3VpnStore.getFreedIdList();
Iterator<Long> it = freeIds.iterator();
if (it.hasNext()) {
value = it.next();
l3VpnStore.removeIdFromFreeList(value);
} else {
value = l3VpnIdGen.getNewId();
}
if (value > ID_LIMIT) {
throw new RuntimeException(ID_LIMIT_EXCEEDED);
}
return CONS_HUNDRED + String.valueOf(value);
}
/**
* Returns the resource id, after constructing model object id and
* converting it.
*/
private void getResourceId() {
ModelObjectId moduleId = ModelObjectId.builder().build();
module = getResourceVal(moduleId);
ModelObjectId svcId = getModIdForL3VpnSvc();
id = getResourceVal(svcId);
ModelObjectId sitesId = getModIdForSites();
sites = getResourceVal(sitesId);
}
/**
* Returns resource id from model converter.
*
* @param modelId model object id
* @return resource id
*/
private ResourceId getResourceVal(ModelObjectId modelId) {
DefaultModelObjectData.Builder data = DefaultModelObjectData.builder()
.identifier(modelId);
ResourceData resData = modelConverter.createDataNode(data.build());
return resData.resourceId();
}
/**
* Processes create request from the store, by taking the root object.
* The root object is then used for l3VPN processing.
*
* @param storeId store resource id
* @param node data node
*/
private void processCreateFromStore(ResourceId storeId, DataNode node) {
if (isElectedLeader) {
List<NodeKey> keys = storeId.nodeKeys();
List<ModelObject> objects = null;
if (keys.size() == 1) {
objects = getModelObjects(node, module);
} else if (keys.size() == 2) {
objects = getModelObjects(node, id);
}
if (objects != null) {
for (ModelObject obj : objects) {
if (obj instanceof DefaultL3VpnSvc) {
DefaultL3VpnSvc l3VpnSvc = (DefaultL3VpnSvc) obj;
createGlobalConfig(l3VpnSvc);
} else if (obj instanceof DefaultSites) {
DefaultSites sites = (DefaultSites) obj;
createInterfaceConfig(sites);
}
}
}
}
}
/**
* Processes delete request from the store, by taking the root object.
* The root object would have got deleted from store. So all the
* configurations are removed.
*
* @param dataNode data node
*/
private void processDeleteFromStore(DataNode dataNode) {
if (isElectedLeader) {
if (dataNode == null) {
//TODO: Delete for inner nodes.
deleteGlobalConfig(null);
}
}
}
/**
* Returns model objects of the store. The data node read from store
* gives the particular node. So the node's parent resource id is taken
* and the data node is given to model converter.
*
* @param dataNode data node from store
* @param appId parent resource id
* @return model objects
*/
public List<ModelObject> getModelObjects(DataNode dataNode,
ResourceId appId) {
ResourceData data = getResourceData(dataNode, appId);
ModelObjectData modelData = modelConverter.createModel(data);
return modelData.modelObjects();
}
/**
* Returns true if the event resource id points to the root level node
* only and event is for addition and deletion; false otherwise.
*
* @param event config event
* @return true if event is supported; false otherwise
*/
public boolean isSupported(DynamicConfigEvent event) {
ResourceId rsId = event.subject();
List<NodeKey> storeKeys = rsId.nodeKeys();
List<NodeKey> regKeys = id.nodeKeys();
List<NodeKey> sitesKeys = sites.nodeKeys();
if (storeKeys != null) {
int storeSize = storeKeys.size();
if (storeSize == 1) {
return storeKeys.get(0).equals(regKeys.get(1)) &&
(event.type() == NODE_ADDED ||
event.type() == NODE_DELETED);
} else if (storeSize == 2) {
return (storeKeys.get(0).equals(sitesKeys.get(1))) &&
storeKeys.get(1).equals(sitesKeys.get(2)) &&
(event.type() == NODE_ADDED ||
event.type() == NODE_DELETED);
}
}
return false;
}
/***
* Creates all configuration in the standard device model.
*
* @param l3VpnSvc l3VPN service object
*/
void createGlobalConfig(L3VpnSvc l3VpnSvc) {
if (l3VpnSvc.vpnServices() != null) {
createVpnServices(l3VpnSvc.vpnServices());
}
if (l3VpnSvc.sites() != null) {
createInterfaceConfig(l3VpnSvc.sites());
}
}
/**
* Creates the VPN instances from the VPN services object, if only that
* VPN instance is not already created.
*
* @param vpnSvcs VPN services object
*/
private void createVpnServices(VpnServices vpnSvcs) {
if (vpnSvcs != null && vpnSvcs.vpnSvc() != null) {
List<VpnSvc> svcList = vpnSvcs.vpnSvc();
for (VpnSvc svc : svcList) {
String vpnName = svc.vpnId().string();
l3VpnStore.addVpnInsIfAbsent(vpnName, new VpnInstance(vpnName));
}
}
}
/**
* Creates interface configuration from the site network access if
* available.
*
* @param sites sites object
*/
private void createInterfaceConfig(Sites sites) {
if (sites.site() != null) {
List<Site> sitesList = sites.site();
for (Site site : sitesList) {
if (site.siteNetworkAccesses() != null) {
SiteNetworkAccesses accesses = site.siteNetworkAccesses();
List<SiteNetworkAccess> accessList =
accesses.siteNetworkAccess();
for (SiteNetworkAccess access : accessList) {
createFromAccess(access, site.siteId().string());
}
}
}
}
}
/**
* Creates the interface and VPN related configurations from the access
* and site id value.
*
* @param access site network access
* @param siteId site id
*/
private void createFromAccess(SiteNetworkAccess access, String siteId) {
Map<AccessInfo, InterfaceInfo> intMap = l3VpnStore.getInterfaceInfo();
Map<String, VpnInstance> insMap = l3VpnStore.getVpnInstances();
String accessId = access.siteNetworkAccessId().string();
AccessInfo info = new AccessInfo(siteId, accessId);
if (intMap.get(info) == null) {
VpnSiteRole siteRole = getSiteRole(access.vpnAttachment());
VpnInstance instance = insMap.get(siteRole.name());
if (instance == null) {
throw new NetL3VpnException(SITE_VPN_MISMATCH);
}
buildFromAccess(instance, info, access, siteRole);
}
}
/**
* Returns the VPN site role from the VPN attachment.
*
* @param attach VPN attachment
* @return VPN site role
*/
private VpnSiteRole getSiteRole(VpnAttachment attach) {
if (attach == null || attach.attachmentFlavor() == null) {
throw new NetL3VpnException(VPN_ATTACHMENT_NULL);
}
AttachmentFlavor flavor = attach.attachmentFlavor();
if (!(flavor instanceof DefaultVpnId)) {
throw new NetL3VpnException(VPN_POLICY_NOT_SUPPORTED);
}
DefaultVpnId vpnId = (DefaultVpnId) flavor;
if (vpnId.siteRole() == null) {
throw new NetL3VpnException(SITE_ROLE_NULL);
}
VpnType role = getRole(vpnId.siteRole());
return new VpnSiteRole(String.valueOf(vpnId.vpnId()), role);
}
/**
* Builds the required details for device standard model from the site
* network access info available.
*
* @param instance VPN instance
* @param info access info
* @param access network access
* @param role VPN site role
*/
private void buildFromAccess(VpnInstance instance, AccessInfo info,
SiteNetworkAccess access, VpnSiteRole role) {
Bearer bearer = access.bearer();
if (bearer == null) {
throw new NetL3VpnException(BEARER_NULL);
}
RequestedType reqType = bearer.requestedType();
IpConnection connect = access.ipConnection();
RoutingProtocols pro = access.routingProtocols();
if (reqType == null || connect == null) {
throw new NetL3VpnException(IP_INT_INFO_NULL);
}
buildDeviceDetails(instance, info, role, bearer, connect,
reqType, pro);
}
/**
* Builds the device details such as, VPN instance value if it is for
* the first time, interface values and BGP info if available in service.
*
* @param instance VPN instance
* @param accInfo access info
* @param role VPN site role
* @param bearer bearer object
* @param connect ip connect object
* @param reqType requested type
* @param pro routing protocol
*/
private void buildDeviceDetails(VpnInstance instance, AccessInfo accInfo,
VpnSiteRole role, Bearer bearer,
IpConnection connect, RequestedType reqType,
RoutingProtocols pro) {
Map<AccessInfo, InterfaceInfo> interMap = l3VpnStore.getInterfaceInfo();
InterfaceInfo intInfo = interMap.get(accInfo);
if (intInfo != null) {
return;
}
DeviceInfo info = buildDevVpnIns(bearer, instance, role, connect);
String portName = getInterfaceName(info, reqType);
buildDevVpnInt(info, instance, connect, portName, accInfo);
if (pro != null && pro.routingProtocol() != null) {
buildBgpInfo(pro.routingProtocol(), info,
role.name(), connect, accInfo);
}
InterfaceInfo interInfo = new InterfaceInfo(info, portName,
instance.vpnName());
l3VpnStore.addInterfaceInfo(accInfo, interInfo);
l3VpnStore.addVpnIns(instance.vpnName(), instance);
}
/**
* Builds device VPN instance with the service objects. It returns
*
* @param bearer bearer object
* @param ins VPN instance
* @param role VPN site role
* @param connect ip connection
* @return return
*/
private DeviceInfo buildDevVpnIns(Bearer bearer, VpnInstance ins,
VpnSiteRole role, IpConnection connect) {
DefaultAugmentedL3VpnBearer augBearer = ((DefaultBearer) bearer)
.augmentation(DefaultAugmentedL3VpnBearer.class);
DeviceId id = getDeviceId(augBearer);
Map<DeviceId, DeviceInfo> devices = ins.devInfo();
DeviceInfo info = null;
if (devices != null) {
info = devices.get(id);
}
if (info == null) {
info = createVpnInstance(id, role, ins, connect);
}
return info;
}
/**
* Returns the device id from the bearer augment attachment of service.
* If the attachment in augment is not available it throws error.
*
* @param attach augmented bearer
* @return device id
*/
private DeviceId getDeviceId(DefaultAugmentedL3VpnBearer attach) {
if (attach == null || attach.bearerAttachment() == null ||
attach.bearerAttachment().peMgmtIp() == null ||
attach.bearerAttachment().peMgmtIp().string() == null) {
throw new NetL3VpnException(DEVICE_INFO_NULL);
}
String ip = attach.bearerAttachment().peMgmtIp().string();
return getId(ip);
}
/**
* Returns the device id whose management ip address matches with the ip
* received.
*
* @param ip ip address
* @return device id
*/
public DeviceId getId(String ip) {
for (Device device : deviceService.getAvailableDevices()) {
String val = device.annotations().value(IP);
if (ip.equals(val)) {
return device.id();
}
}
throw new NetL3VpnException(getMgmtIpUnAvailErr(ip));
}
/**
* Creates the VPN instance by constructing standard device model of
* instances. It adds the RD and RT values to the VPN instance.
*
* @param id device id
* @param role VPN site role
* @param inst VPN instance
* @param ip ip connection
* @return device info
*/
private DeviceInfo createVpnInstance(DeviceId id, VpnSiteRole role,
VpnInstance inst, IpConnection ip) {
Map<AccessInfo, InterfaceInfo> intMap = l3VpnStore.getInterfaceInfo();
generateRdRt(inst, role);
DeviceInfo info = new DeviceInfo(id);
NetworkInstances instances = createInstance(inst, role, ip);
ModelObjectData devMod = getVpnCreateModObj(intMap, instances,
id.toString());
ModelObjectData driMod = info.processCreateInstance(driverService,
devMod);
ResourceData resData = modelConverter.createDataNode(driMod);
addToStore(resData);
l3VpnStore.addVpnIns(inst.vpnName(), inst);
inst.addDevInfo(id, info);
return info;
}
/**
* Adds the resource data that is received from the driver, after
* converting from the model object data.
*
* @param resData resource data
*/
private void addToStore(ResourceData resData) {
if (resData != null && resData.dataNodes() != null) {
List<DataNode> dataNodes = resData.dataNodes();
for (DataNode node : dataNodes) {
configService.createNodeRecursive(resData.resourceId(), node);
}
}
}
/**
* Generates RD and RT value for the VPN instance for the first time VPN
* instance creation.
*
* @param ins VPN instance
* @param role VPN site role
*/
private void generateRdRt(VpnInstance ins, VpnSiteRole role) {
ins.type(role.role());
VpnConfig config = ins.vpnConfig();
String rd = null;
if (config == null) {
rd = getIdFromGen();
}
switch (ins.type()) {
case ANY_TO_ANY:
if (config == null) {
config = new FullMeshVpnConfig(rd);
config.rd(rd);
}
break;
case HUB:
case SPOKE:
if (config == null) {
config = new HubSpokeVpnConfig();
config.rd(rd);
}
createImpRtVal((HubSpokeVpnConfig) config, ins.type());
createExpRtVal((HubSpokeVpnConfig) config, ins.type());
break;
default:
throw new NetL3VpnException(VPN_TYPE_UNSUPPORTED);
}
ins.vpnConfig(config);
}
/**
* Creates import RT value for HUB and SPOKE, according to the type, if
* the values are not present.
*
* @param config VPN config
* @param type VPN type
*/
private void createImpRtVal(HubSpokeVpnConfig config, VpnType type) {
if (type == HUB) {
if (config.hubImpRt() != null) {
return;
}
setHubImpRt(config);
} else {
if (config.spokeImpRt() != null) {
return;
}
config.spokeImpRt(config.rd());
}
}
/**
* Sets the HUB import RT, from the spoke export RT. If it is not
* available a new ID is generated.
*
* @param config VPN config
*/
public void setHubImpRt(HubSpokeVpnConfig config) {
String hubImp;
if (config.spokeExpRt() != null) {
hubImp = config.spokeExpRt();
} else {
hubImp = getIdFromGen();
}
config.hubImpRt(hubImp);
}
/**
* Creates export RT value for HUB and SPOKE, according to the type, if
* the values are not present.
*
* @param config VPN config
* @param type VPN type
*/
private void createExpRtVal(HubSpokeVpnConfig config, VpnType type) {
if (type == HUB) {
if (config.hubExpRt() != null) {
return;
}
config.hubExpRt(config.rd());
} else {
if (config.spokeExpRt() != null) {
return;
}
setSpokeExpRt(config);
}
}
/**
* Sets the SPOKE export RT, from the hub import RT. If it is not
* available a new ID is generated.
*
* @param config VPN config
*/
public void setSpokeExpRt(HubSpokeVpnConfig config) {
String spokeExp;
if (config.hubImpRt() != null) {
spokeExp = config.hubImpRt();
} else {
spokeExp = getIdFromGen();
}
config.spokeExpRt(spokeExp);
}
/**
* Returns the interface name from the requested type service object.
*
* @param info device info
* @param reqType requested type
* @return interface name
*/
private String getInterfaceName(DeviceInfo info, RequestedType reqType) {
DefaultAugmentedL3VpnRequestedType req =
((DefaultRequestedType) reqType).augmentation(
DefaultAugmentedL3VpnRequestedType.class);
if (req == null || req.requestedTypeProfile() == null ||
req.requestedTypeProfile().requestedTypeChoice() == null) {
throw new NetL3VpnException(INT_INFO_NULL);
}
RequestedTypeChoice reqChoice = req.requestedTypeProfile()
.requestedTypeChoice();
return getNameFromChoice(reqChoice, info.deviceId());
}
/**
* Returns the interface name from the type choice provided.
*
* @param choice service choice
* @param id device id
* @return interface name
*/
private String getNameFromChoice(RequestedTypeChoice choice, DeviceId id) {
if (choice == null) {
throw new NetL3VpnException(INT_INFO_NULL);
}
String intName;
if (choice instanceof DefaultDot1Qcase) {
if (((DefaultDot1Qcase) choice).dot1q() == null ||
((DefaultDot1Qcase) choice).dot1q()
.physicalIf() == null) {
throw new NetL3VpnException(INT_INFO_NULL);
}
intName = ((DefaultDot1Qcase) choice).dot1q().physicalIf();
} else {
if (((DefaultPhysicalCase) choice).physical() == null ||
((DefaultPhysicalCase) choice).physical()
.physicalIf() == null) {
throw new NetL3VpnException(INT_INFO_NULL);
}
intName = ((DefaultPhysicalCase) choice).physical().physicalIf();
}
return getPortName(intName, id);
}
/**
* Returns the port name when it the port is available in the device.
*
* @param intName interface name
* @param id device id
* @return port name
*/
private String getPortName(String intName, DeviceId id) {
List<Port> ports = deviceService.getPorts(id);
for (Port port : ports) {
String pName = port.annotations().value(PORT_NAME);
if (pName.equals(intName)) {
return intName;
}
}
throw new NetL3VpnException(getIntNotAvailable(intName));
}
/**
* Builds the interface for the device binding with the VPN instance.
*
* @param info device info
* @param ins VPN instance
* @param connect IP connection
* @param pName port name
* @param access access info
*/
private void buildDevVpnInt(DeviceInfo info, VpnInstance ins,
IpConnection connect, String pName,
AccessInfo access) {
Map<AccessInfo, InterfaceInfo> intMap = l3VpnStore.getInterfaceInfo();
info.addAccessInfo(access);
info.addIfName(pName);
Interfaces interfaces = createInterface(pName, ins.vpnName(),
connect);
ModelObjectData devMod = getIntCreateModObj(
info.ifNames(), interfaces, info.deviceId().toString());
ModelObjectData driMod = info.processCreateInterface(driverService,
devMod);
ResourceData resData = modelConverter.createDataNode(driMod);
addToStore(resData);
}
/**
* Builds the BGP information from the routes that are given from the
* service.
*
* @param routes routing protocol
* @param info device info
* @param name VPN name
* @param connect IP connection
* @param access access info
*/
private void buildBgpInfo(List<RoutingProtocol> routes, DeviceInfo info,
String name, IpConnection connect,
AccessInfo access) {
Map<BgpInfo, DeviceId> bgpMap = l3VpnStore.getBgpInfo();
BgpInfo intBgp = createBgpInfo(routes, info, name, connect, access);
if (intBgp != null) {
intBgp.vpnName(name);
BgpDriverInfo config = getBgpCreateConfigObj(
bgpMap, info.deviceId().toString(), info.bgpInfo(), intBgp);
ModelObjectData driData = info.processCreateBgpInfo(
driverService, intBgp, config);
l3VpnStore.addBgpInfo(info.bgpInfo(), info.deviceId());
ResourceData resData = modelConverter.createDataNode(driData);
addToStore(resData);
}
}
/**
* Creates all configuration in the standard device model.
*
* @param l3VpnSvc l3 VPN service
*/
void deleteGlobalConfig(L3VpnSvc l3VpnSvc) {
deleteGlobalVpn(l3VpnSvc);
//TODO: Site and access deletion needs to be added.
}
/**
* Deletes the global VPN from the device model and delete from the device.
*
* @param l3VpnSvc L3 VPN service
*/
private void deleteGlobalVpn(L3VpnSvc l3VpnSvc) {
Map<String, VpnInstance> insMap = l3VpnStore.getVpnInstances();
//TODO: check for VPN delete deleting interface from store.
if (l3VpnSvc == null || l3VpnSvc.vpnServices() == null ||
l3VpnSvc.vpnServices().vpnSvc() == null) {
for (Map.Entry<String, VpnInstance> vpnMap : insMap.entrySet()) {
deleteVpnInstance(vpnMap.getValue(), false);
}
return;
}
List<VpnSvc> vpnList = l3VpnSvc.vpnServices().vpnSvc();
for (Map.Entry<String, VpnInstance> vpnMap : insMap.entrySet()) {
boolean isPresent = isVpnPresent(vpnMap.getKey(), vpnList);
if (!isPresent) {
deleteVpnInstance(vpnMap.getValue(), false);
}
}
}
/**
* Returns true if the VPN in the distributed map is also present in the
* service; false otherwise.
*
* @param vpnName VPN name from store
* @param vpnList VPN list from service
* @return true if VPN available; false otherwise
*/
private boolean isVpnPresent(String vpnName, List<VpnSvc> vpnList) {
for (VpnSvc svc : vpnList) {
if (svc.vpnId().string().equals(vpnName)) {
return true;
}
}
return false;
}
/**
* Deletes the VPN instance by constructing standard device model of
* instances.
*
* @param instance VPN instance
* @param isIntDeleted if interface already removed.
*/
private void deleteVpnInstance(VpnInstance instance, boolean isIntDeleted) {
Map<DeviceId, DeviceInfo> devices = instance.devInfo();
if (devices != null) {
for (Map.Entry<DeviceId, DeviceInfo> device : devices.entrySet()) {
NetworkInstances ins = deleteInstance(instance.vpnName());
DeviceInfo dev = device.getValue();
if (!isIntDeleted) {
remVpnBgp(dev);
remInterfaceFromMap(dev);
}
Map<AccessInfo, InterfaceInfo> intMap =
l3VpnStore.getInterfaceInfo();
String id = dev.deviceId().toString();
ModelObjectData devMod = getVpnDelModObj(intMap, ins, id);
ModelObjectData driMod = dev.processDeleteInstance(
driverService, devMod);
ResourceData resData = modelConverter.createDataNode(driMod);
deleteFromStore(resData);
}
l3VpnStore.removeVpnInstance(instance.vpnName());
}
}
/**
* Removes the BGP information for that complete VPN instance.
*
* @param dev device info
*/
private void remVpnBgp(DeviceInfo dev) {
BgpInfo devBgp = dev.bgpInfo();
if (devBgp != null) {
l3VpnStore.removeBgpInfo(devBgp);
BgpInfo delInfo = new BgpInfo();
delInfo.vpnName(devBgp.vpnName());
String id = dev.deviceId().toString();
Map<BgpInfo, DeviceId> bgpMap = l3VpnStore.getBgpInfo();
BgpDriverInfo driConfig = getVpnBgpDelModObj(bgpMap, id);
ModelObjectData driData = dev.processDeleteBgpInfo(
driverService, delInfo, driConfig);
ResourceData resData = modelConverter.createDataNode(driData);
deleteFromStore(resData);
l3VpnStore.removeBgpInfo(devBgp);
}
}
/**
* Deletes the resource data that is received from the driver, after
* converting from the model object data.
*
* @param resData resource data
*/
private void deleteFromStore(ResourceData resData) {
if (resData != null) {
configService.deleteNodeRecursive(resData.resourceId());
}
}
/**
* Removes the interface from the app distributed map, if the driver
* interfaces are already removed from the store.
*
* @param deviceInfo device info
*/
private void remInterfaceFromMap(DeviceInfo deviceInfo) {
List<AccessInfo> accesses = deviceInfo.accesses();
if (accesses != null) {
for (AccessInfo access : accesses) {
l3VpnStore.removeInterfaceInfo(access);
}
}
deviceInfo.ifNames(null);
deviceInfo.accesses(null);
}
/**
* Signals that the leadership has changed.
*
* @param isLeader true if this instance is now the leader, otherwise false
*/
private void leaderChanged(boolean isLeader) {
log.debug("Leader changed: {}", isLeader);
isElectedLeader = isLeader;
}
/**
* Representation of internal listener, listening for dynamic config event.
*/
private class InternalConfigListener implements DynamicConfigListener {
@Override
public boolean isRelevant(DynamicConfigEvent event) {
return isSupported(event);
}
@Override
public void event(DynamicConfigEvent event) {
accumulator.add(event);
}
}
/**
* Accumulates events to allow processing after a desired number of
* events were accumulated.
*/
private class InternalEventAccumulator extends
AbstractAccumulator<DynamicConfigEvent> {
/**
* Constructs the event accumulator with timer and event limit.
*/
protected InternalEventAccumulator() {
super(new Timer(TIMER), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
}
@Override
public void processItems(List<DynamicConfigEvent> events) {
for (DynamicConfigEvent event : events) {
checkNotNull(event, EVENT_NULL);
Filter filter = new Filter();
DataNode node;
try {
node = configService.readNode(event.subject(), filter);
} catch (FailedException e) {
node = null;
}
switch (event.type()) {
case NODE_ADDED:
processCreateFromStore(event.subject(), node);
break;
case NODE_DELETED:
processDeleteFromStore(node);
break;
default:
log.warn(UNKNOWN_EVENT, event.type());
break;
}
}
}
}
/**
* A listener for leadership events.
*/
private class InternalLeadershipListener implements LeadershipEventListener {
@Override
public boolean isRelevant(LeadershipEvent event) {
return event.subject().topic().equals(appId.name());
}
@Override
public void event(LeadershipEvent event) {
switch (event.type()) {
case LEADER_CHANGED:
case LEADER_AND_CANDIDATES_CHANGED:
if (localNodeId.equals(event.subject().leaderNodeId())) {
log.info("Net l3vpn manager gained leadership");
leaderChanged(true);
} else {
log.info("Net l3vpn manager leader changed. New " +
"leader is {}", event.subject()
.leaderNodeId());
leaderChanged(false);
}
default:
break;
}
}
}
}