blob: 18ff1727103ef1aa8e089fe2698b7b648d245608 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* 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.evpnopenflow.rsc.vpnport.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Sets;
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.apache.felix.scr.annotations.Service;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.evpnopenflow.rsc.BasePort;
import org.onosproject.evpnopenflow.rsc.BasePortId;
import org.onosproject.evpnopenflow.rsc.DefaultVpnPort;
import org.onosproject.evpnopenflow.rsc.VpnInstanceId;
import org.onosproject.evpnopenflow.rsc.VpnPort;
import org.onosproject.evpnopenflow.rsc.VpnPortId;
import org.onosproject.evpnopenflow.rsc.baseport.BasePortService;
import org.onosproject.evpnopenflow.rsc.vpnport.VpnPortEvent;
import org.onosproject.evpnopenflow.rsc.vpnport.VpnPortListener;
import org.onosproject.evpnopenflow.rsc.vpnport.VpnPortService;
import org.onosproject.net.DeviceId;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
import org.onosproject.vtnrsc.AllocationPool;
import org.onosproject.vtnrsc.BindingHostId;
import org.onosproject.vtnrsc.DefaultSubnet;
import org.onosproject.vtnrsc.DefaultTenantNetwork;
import org.onosproject.vtnrsc.DefaultVirtualPort;
import org.onosproject.vtnrsc.FixedIp;
import org.onosproject.vtnrsc.HostRoute;
import org.onosproject.vtnrsc.PhysicalNetwork;
import org.onosproject.vtnrsc.SegmentationId;
import org.onosproject.vtnrsc.Subnet;
import org.onosproject.vtnrsc.SubnetId;
import org.onosproject.vtnrsc.TenantId;
import org.onosproject.vtnrsc.TenantNetwork;
import org.onosproject.vtnrsc.TenantNetworkId;
import org.onosproject.vtnrsc.VirtualPort;
import org.onosproject.vtnrsc.VirtualPortId;
import org.onosproject.vtnrsc.subnet.SubnetService;
import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService;
import org.onosproject.vtnrsc.virtualport.VirtualPortService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.APP_ID;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.DELETE;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.EVENT_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.EVPN_VPN_PORT_START;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.EVPN_VPN_PORT_STOP;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.INTERFACE_ID;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.JSON_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.LISTENER_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.RESPONSE_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.SET;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.SLASH;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.UPDATE;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_INSTANCE;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_CREATION_FAILED;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_DELETE_FAILED;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_ID;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_ID_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_IS_NOT_EXIST;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_STORE;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_UPDATE_FAILED;
/**
* Provides implementation of the VpnPort service.
*/
@Component(immediate = true)
@Service
public class VpnPortManager implements VpnPortService {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Set<VpnPortListener> listeners = Sets
.newCopyOnWriteArraySet();
protected EventuallyConsistentMap<VpnPortId, VpnPort> vpnPortStore;
protected ApplicationId appId;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected BasePortService basePortService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected VirtualPortService virtualPortService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected TenantNetworkService tenantNetworkService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected SubnetService subnetService;
@Activate
public void activate() {
appId = coreService.registerApplication(APP_ID);
KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API).register(VpnPort.class)
.register(VpnPortId.class);
vpnPortStore = storageService
.<VpnPortId, VpnPort>eventuallyConsistentMapBuilder()
.withName(VPN_PORT_STORE).withSerializer(serializer)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
log.info(EVPN_VPN_PORT_START);
}
@Deactivate
public void deactivate() {
vpnPortStore.destroy();
log.info(EVPN_VPN_PORT_STOP);
}
@Override
public boolean exists(VpnPortId vpnPortId) {
checkNotNull(vpnPortId, VPN_PORT_ID_NOT_NULL);
return vpnPortStore.containsKey(vpnPortId);
}
@Override
public VpnPort getPort(VpnPortId vpnPortId) {
checkNotNull(vpnPortId, VPN_PORT_ID_NOT_NULL);
return vpnPortStore.get(vpnPortId);
}
@Override
public Collection<VpnPort> getPorts() {
return Collections.unmodifiableCollection(vpnPortStore.values());
}
@Override
public boolean createPorts(Iterable<VpnPort> vpnPorts) {
checkNotNull(vpnPorts, VPN_PORT_NOT_NULL);
for (VpnPort vpnPort : vpnPorts) {
log.info(VPN_PORT_ID, vpnPort.id().toString());
vpnPortStore.put(vpnPort.id(), vpnPort);
if (!vpnPortStore.containsKey(vpnPort.id())) {
log.info(VPN_PORT_CREATION_FAILED, vpnPort.id().toString());
return false;
}
notifyListeners(new VpnPortEvent(VpnPortEvent.Type.VPN_PORT_SET,
vpnPort));
}
return true;
}
@Override
public boolean updatePorts(Iterable<VpnPort> vpnPorts) {
checkNotNull(vpnPorts, VPN_PORT_NOT_NULL);
for (VpnPort vpnPort : vpnPorts) {
if (!vpnPortStore.containsKey(vpnPort.id())) {
log.info(VPN_PORT_IS_NOT_EXIST, vpnPort.id().toString());
return false;
}
vpnPortStore.put(vpnPort.id(), vpnPort);
if (!vpnPort.equals(vpnPortStore.get(vpnPort.id()))) {
log.info(VPN_PORT_UPDATE_FAILED, vpnPort.id().toString());
return false;
}
notifyListeners(new VpnPortEvent(VpnPortEvent.Type.VPN_PORT_UPDATE,
vpnPort));
}
return true;
}
@Override
public boolean removePorts(Iterable<VpnPortId> vpnPortIds) {
checkNotNull(vpnPortIds, VPN_PORT_NOT_NULL);
for (VpnPortId vpnPortid : vpnPortIds) {
VpnPort vpnPort = vpnPortStore.get(vpnPortid);
vpnPortStore.remove(vpnPortid);
if (vpnPortStore.containsKey(vpnPortid)) {
log.info(VPN_PORT_DELETE_FAILED, vpnPortid.toString());
return false;
}
notifyListeners(new VpnPortEvent(VpnPortEvent.Type.VPN_PORT_DELETE,
vpnPort));
}
return true;
}
@Override
public void processGluonConfig(String action, String key, JsonNode value) {
Collection<VpnPort> vpnPorts;
switch (action) {
case DELETE:
String[] list = key.split(SLASH);
VpnPortId vpnPortId
= VpnPortId.vpnPortId(list[list.length - 1]);
Set<VpnPortId> vpnPortIds = Sets.newHashSet(vpnPortId);
removePorts(vpnPortIds);
// After removing vpn port and also remove virtual port from vtn
VirtualPortId virtualPortId
= VirtualPortId.portId(list[list.length - 1]);
Set<VirtualPortId> virtualPortIds
= Sets.newHashSet(virtualPortId);
virtualPortService.removePorts(virtualPortIds);
break;
case SET:
checkNotNull(value, RESPONSE_NOT_NULL);
vpnPorts = changeJsonToSub(value);
createPorts(vpnPorts);
break;
case UPDATE:
checkNotNull(value, RESPONSE_NOT_NULL);
vpnPorts = changeJsonToSub(value);
updatePorts(vpnPorts);
break;
default:
log.info("Invalid action is received while processing VPN " +
"port configuration");
}
}
/**
* Creates dummy gluon network to the VTN.
*
* @param state the base port state
* @param adminStateUp the base port admin status
* @param tenantID the base port tenant ID
*/
private void createDummyGluonNetwork(boolean adminStateUp, String state,
TenantId tenantID) {
String id = "11111111-1111-1111-1111-111111111111";
String name = "GluonNetwork";
String segmentationID = "50";
String physicalNetwork = "None";
TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name,
adminStateUp,
TenantNetwork.State.valueOf(state),
false, tenantID,
false,
TenantNetwork.Type.LOCAL,
PhysicalNetwork.physicalNetwork(physicalNetwork),
SegmentationId.segmentationId(segmentationID));
Set<TenantNetwork> networksSet = Sets.newHashSet(network);
tenantNetworkService.createNetworks(networksSet);
}
/**
* Creates dummy gluon subnet to the VTN.
*
* @param tenantId the base port tenant ID
*/
public void createDummySubnet(TenantId tenantId) {
String id = "22222222-2222-2222-2222-222222222222";
String subnetName = "GluonSubnet";
String cidr = "0.0.0.0/0";
String gatewayIp = "0.0.0.0";
Set<HostRoute> hostRoutes = Sets.newHashSet();
String ipV6AddressMode = null;
String ipV6RaMode = null;
TenantNetworkId tenantNetworkId = null;
Set<AllocationPool> allocationPools = Sets.newHashSet();
Iterable<TenantNetwork> networks
= tenantNetworkService.getNetworks();
for (TenantNetwork tenantNetwork : networks) {
if (tenantNetwork.name().equals("GluonNetwork")) {
tenantNetworkId = tenantNetwork.id();
break;
}
}
Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName,
tenantNetworkId,
tenantId, IpAddress.Version.INET,
cidr == null ? null : IpPrefix.valueOf(cidr),
gatewayIp == null ? null : IpAddress.valueOf(gatewayIp),
false, false, hostRoutes,
ipV6AddressMode == null ? null : Subnet.Mode.valueOf(ipV6AddressMode),
ipV6RaMode == null ? null : Subnet.Mode.valueOf(ipV6RaMode),
allocationPools);
Set<Subnet> subnetsSet = Sets.newHashSet(subnet);
subnetService.createSubnets(subnetsSet);
}
/**
* Returns a collection of vpnPort from subnetNodes.
*
* @param vpnPortNodes the vpnPort json node
* @return list of vpnports
*/
private Collection<VpnPort> changeJsonToSub(JsonNode vpnPortNodes) {
checkNotNull(vpnPortNodes, JSON_NOT_NULL);
Map<VpnPortId, VpnPort> vpnPortMap = new HashMap<>();
String interfaceId = vpnPortNodes.get(INTERFACE_ID).asText();
VpnPortId vpnPortId = VpnPortId.vpnPortId(interfaceId);
VpnInstanceId vpnInstanceId = VpnInstanceId
.vpnInstanceId(vpnPortNodes.get(VPN_INSTANCE).asText());
VpnPort vpnPort = new DefaultVpnPort(vpnPortId, vpnInstanceId);
vpnPortMap.put(vpnPortId, vpnPort);
// update ip address and tenant network information in vtn
TenantNetworkId tenantNetworkId = null;
Map<VirtualPortId, VirtualPort> vPortMap = new HashMap<>();
BasePortId basePortId = BasePortId.portId(interfaceId);
VirtualPortId virtualPortId = VirtualPortId.portId(interfaceId);
BasePort bPort = basePortService.getPort(basePortId);
if (bPort != null) {
FixedIp fixedIp = FixedIp.fixedIp(SubnetId.subnetId(basePortId.toString()),
IpAddress.valueOf(vpnPortNodes
.get("ipaddress").asText()));
Set<FixedIp> fixedIps = new HashSet<>();
fixedIps.add(fixedIp);
Map<String, String> strMap = new HashMap<>();
boolean adminStateUp = bPort.adminStateUp();
strMap.put("name", bPort.name());
strMap.put("deviceOwner", bPort.deviceOwner());
strMap.put("bindingVnicType", bPort.bindingVnicType());
strMap.put("bindingVifType", bPort.bindingVifType());
strMap.put("bindingVifDetails", bPort.bindingVifDetails());
String state = bPort.state();
MacAddress macAddress = bPort.macAddress();
TenantId tenantId = bPort.tenantId();
DeviceId deviceId = bPort.deviceId();
BindingHostId bindingHostId = bPort.bindingHostId();
// Creates Dummy Gluon Network and Subnet
createDummyGluonNetwork(adminStateUp, state, tenantId);
createDummySubnet(tenantId);
Iterable<TenantNetwork> networks
= tenantNetworkService.getNetworks();
for (TenantNetwork tenantNetwork : networks) {
if (tenantNetwork.name().equals("GluonNetwork")) {
tenantNetworkId = tenantNetwork.id();
break;
}
}
if (tenantNetworkId != null) {
DefaultVirtualPort vPort = new DefaultVirtualPort(virtualPortId,
tenantNetworkId,
adminStateUp,
strMap, isState(state),
macAddress, tenantId,
deviceId, fixedIps,
bindingHostId,
null,
null);
vPortMap.put(virtualPortId, vPort);
Collection<VirtualPort> virtualPorts
= Collections.unmodifiableCollection(vPortMap.values());
virtualPortService.createPorts(virtualPorts);
}
}
return Collections.unmodifiableCollection(vpnPortMap.values());
}
/**
* Returns BasePort State.
*
* @param state the base port state
* @return the basePort state
*/
private VirtualPort.State isState(String state) {
if (state.equals("ACTIVE")) {
return VirtualPort.State.ACTIVE;
} else {
return VirtualPort.State.DOWN;
}
}
@Override
public void addListener(VpnPortListener listener) {
checkNotNull(listener, LISTENER_NOT_NULL);
listeners.add(listener);
}
@Override
public void removeListener(VpnPortListener listener) {
checkNotNull(listener, LISTENER_NOT_NULL);
listeners.remove(listener);
}
/**
* Notifies specify event to all listeners.
*
* @param event Vpn Port event
*/
private void notifyListeners(VpnPortEvent event) {
checkNotNull(event, EVENT_NOT_NULL);
listeners.forEach(listener -> {
listener.event(event);
});
}
}