blob: 27f9fd7ae1f1bf2f4b79eee3866f0075180769f2 [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.baseport.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Sets;
import org.onlab.packet.IpAddress;
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.DefaultBasePort;
import org.onosproject.evpnopenflow.rsc.baseport.BasePortEvent;
import org.onosproject.evpnopenflow.rsc.baseport.BasePortListener;
import org.onosproject.evpnopenflow.rsc.baseport.BasePortService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.store.service.MultiValuedTimestamp;
import org.onosproject.store.service.StorageService;
import org.onosproject.store.service.WallClockTimestamp;
import org.onosproject.vtnrsc.AllowedAddressPair;
import org.onosproject.vtnrsc.BindingHostId;
import org.onosproject.vtnrsc.DefaultFloatingIp;
import org.onosproject.vtnrsc.FixedIp;
import org.onosproject.vtnrsc.FloatingIp;
import org.onosproject.vtnrsc.FloatingIpId;
import org.onosproject.vtnrsc.RouterId;
import org.onosproject.vtnrsc.SecurityGroup;
import org.onosproject.vtnrsc.SubnetId;
import org.onosproject.vtnrsc.TenantId;
import org.onosproject.vtnrsc.TenantNetwork;
import org.onosproject.vtnrsc.TenantNetworkId;
import org.onosproject.vtnrsc.TenantRouter;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.APP_ID;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.BASE_PORT_STORE;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.LISTENER_NOT_NULL;
import static org.onosproject.evpnopenflow.rsc.EvpnConstants.RESPONSE_NOT_NULL;
/**
* Provides implementation of the BasePort APIs.
*/
@Component(immediate = true, service = BasePortService.class)
public class BasePortManager implements BasePortService {
private final Set<BasePortListener> listeners = Sets
.newCopyOnWriteArraySet();
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String BASEPORT_ID_NULL = "BasePort ID cannot be " +
"null";
private static final String BASEPORT_NOT_NULL = "BasePort cannot be " +
"null";
private static final String TENANTID_NOT_NULL = "TenantId cannot be null";
private static final String NETWORKID_NOT_NULL = "NetworkId cannot be null";
private static final String DEVICEID_NOT_NULL = "DeviceId cannot be null";
private static final String FIXEDIP_NOT_NULL = "FixedIp cannot be null";
private static final String MAC_NOT_NULL = "Mac address cannot be null";
private static final String IP_NOT_NULL = "Ip cannot be null";
private static final String EVENT_NOT_NULL = "event cannot be null";
private static final String SET = "set";
private static final String UPDATE = "update";
private static final String DELETE = "delete";
private static final String SLASH = "/";
private static final String PROTON_BASE_PORT = "Port";
private static final String JSON_NOT_NULL = "JsonNode can not be null";
protected EventuallyConsistentMap<BasePortId, BasePort> vPortStore;
protected ApplicationId appId;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Activate
public void activate() {
appId = coreService.registerApplication(APP_ID);
KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
.register(MultiValuedTimestamp.class)
.register(TenantNetworkId.class)
.register(Host.class)
.register(TenantNetwork.class)
.register(TenantNetworkId.class)
.register(TenantId.class)
.register(SubnetId.class)
.register(BasePortId.class)
.register(BasePort.State.class)
.register(AllowedAddressPair.class)
.register(FixedIp.class)
.register(FloatingIp.class)
.register(FloatingIpId.class)
.register(FloatingIp.Status.class)
.register(UUID.class)
.register(DefaultFloatingIp.class)
.register(BindingHostId.class)
.register(SecurityGroup.class)
.register(IpAddress.class)
.register(DefaultBasePort.class)
.register(RouterId.class)
.register(TenantRouter.class)
.register(BasePort.class);
vPortStore = storageService
.<BasePortId, BasePort>eventuallyConsistentMapBuilder()
.withName(BASE_PORT_STORE).withSerializer(serializer)
.withTimestampProvider((k, v) -> new WallClockTimestamp())
.build();
log.info("Started");
}
@Deactivate
public void deactivate() {
vPortStore.destroy();
log.info("Stoppped");
}
@Override
public boolean exists(BasePortId vPortId) {
checkNotNull(vPortId, BASEPORT_ID_NULL);
return vPortStore.containsKey(vPortId);
}
@Override
public BasePort getPort(BasePortId vPortId) {
checkNotNull(vPortId, BASEPORT_ID_NULL);
return vPortStore.get(vPortId);
}
@Override
public BasePort getPort(FixedIp fixedIP) {
checkNotNull(fixedIP, FIXEDIP_NOT_NULL);
List<BasePort> vPorts = new ArrayList<>();
vPortStore.values().forEach(p -> {
for (FixedIp fixedIp : p.fixedIps()) {
if (fixedIp.equals(fixedIP)) {
vPorts.add(p);
break;
}
}
});
if (vPorts.size() == 0) {
return null;
}
return vPorts.get(0);
}
@Override
public BasePort getPort(MacAddress mac) {
checkNotNull(mac, MAC_NOT_NULL);
List<BasePort> vPorts = new ArrayList<>();
vPortStore.values().forEach(p -> {
if (p.macAddress().equals(mac)) {
vPorts.add(p);
}
});
if (vPorts.size() == 0) {
return null;
}
return vPorts.get(0);
}
@Override
public BasePort getPort(TenantNetworkId networkId, IpAddress ip) {
checkNotNull(networkId, NETWORKID_NOT_NULL);
checkNotNull(ip, IP_NOT_NULL);
List<BasePort> vPorts = new ArrayList<>();
vPortStore.values().stream().filter(p -> p.networkId().equals(networkId))
.forEach(p -> {
for (FixedIp fixedIp : p.fixedIps()) {
if (fixedIp.ip().equals(ip)) {
vPorts.add(p);
break;
}
}
});
if (vPorts.size() == 0) {
return null;
}
return vPorts.get(0);
}
@Override
public Collection<BasePort> getPorts() {
return Collections.unmodifiableCollection(vPortStore.values());
}
@Override
public Collection<BasePort> getPorts(TenantNetworkId networkId) {
checkNotNull(networkId, NETWORKID_NOT_NULL);
return vPortStore.values().stream().filter(d -> d.networkId().equals(networkId))
.collect(Collectors.toList());
}
@Override
public Collection<BasePort> getPorts(TenantId tenantId) {
checkNotNull(tenantId, TENANTID_NOT_NULL);
return vPortStore.values().stream().filter(d -> d.tenantId().equals(tenantId))
.collect(Collectors.toList());
}
@Override
public Collection<BasePort> getPorts(DeviceId deviceId) {
checkNotNull(deviceId, DEVICEID_NOT_NULL);
return vPortStore.values().stream().filter(d -> d.deviceId().equals(deviceId))
.collect(Collectors.toList());
}
@Override
public boolean createPorts(Iterable<BasePort> vPorts) {
checkNotNull(vPorts, BASEPORT_NOT_NULL);
for (BasePort vPort : vPorts) {
log.info("vPortId is {} ", vPort.portId().toString());
vPortStore.put(vPort.portId(), vPort);
if (!vPortStore.containsKey(vPort.portId())) {
log.info("The basePort is created failed whose identifier is" +
" {} ",
vPort.portId().toString());
return false;
}
}
return true;
}
@Override
public boolean updatePorts(Iterable<BasePort> vPorts) {
checkNotNull(vPorts, BASEPORT_NOT_NULL);
for (BasePort vPort : vPorts) {
vPortStore.put(vPort.portId(), vPort);
if (!vPortStore.containsKey(vPort.portId())) {
log.info("The basePort is not exist whose identifier is {}",
vPort.portId().toString());
return false;
}
vPortStore.put(vPort.portId(), vPort);
if (!vPort.equals(vPortStore.get(vPort.portId()))) {
log.info("The basePort is updated failed whose identifier " +
"is {}",
vPort.portId().toString());
return false;
}
}
return true;
}
@Override
public boolean removePorts(Iterable<BasePortId> vPortIds) {
checkNotNull(vPortIds, BASEPORT_ID_NULL);
for (BasePortId vPortId : vPortIds) {
vPortStore.remove(vPortId);
if (vPortStore.containsKey(vPortId)) {
log.info("The basePort is removed failed whose identifier is" +
" {}",
vPortId.toString());
return false;
}
}
return true;
}
/**
* Returns a collection of basePorts from subnetNodes.
*
* @param vPortNodes the basePort json node
* @return BasePort collection of vpn ports
*/
private Collection<BasePort> changeJsonToSub(JsonNode vPortNodes) {
checkNotNull(vPortNodes, JSON_NOT_NULL);
Set<FixedIp> fixedIps = null;
TenantNetworkId tenantNetworkId = null;
Map<BasePortId, BasePort> vportMap = new HashMap<>();
Map<String, String> strMap = new HashMap<>();
BasePortId basePortId = BasePortId.portId(vPortNodes.get("id").asText());
String name = vPortNodes.get("name").asText();
TenantId tenantId = TenantId
.tenantId(vPortNodes.get("tenant_id").asText());
Boolean adminStateUp = vPortNodes.get("admin_state_up").asBoolean();
String state = vPortNodes.get("status").asText();
MacAddress macAddress = MacAddress
.valueOf(vPortNodes.get("mac_address").asText());
DeviceId deviceId = DeviceId
.deviceId(vPortNodes.get("device_id").asText());
String deviceOwner = vPortNodes.get("device_owner").asText();
BindingHostId bindingHostId = BindingHostId
.bindingHostId(vPortNodes.get("host_id").asText());
String bindingVnicType = vPortNodes.get("vnic_type").asText();
String bindingVifType = vPortNodes.get("vif_type").asText();
String bindingVifDetails = vPortNodes.get("vif_details").asText();
strMap.put("name", name);
strMap.put("deviceOwner", deviceOwner);
strMap.put("bindingVnicType", bindingVnicType);
strMap.put("bindingVifType", bindingVifType);
strMap.put("bindingVifDetails", bindingVifDetails);
BasePort prevBasePort = getPort(basePortId);
if (prevBasePort != null) {
fixedIps = prevBasePort.fixedIps();
tenantNetworkId = prevBasePort.networkId();
}
BasePort vPort = new DefaultBasePort(basePortId,
tenantNetworkId,
adminStateUp,
strMap, state,
macAddress, tenantId,
deviceId, fixedIps,
bindingHostId,
null,
null);
vportMap.put(basePortId, vPort);
return Collections.unmodifiableCollection(vportMap.values());
}
/**
* Returns BasePort State.
*
* @param state the base port state
* @return the basePort state
*/
private BasePort.State isState(String state) {
if (state.equals("ACTIVE")) {
return BasePort.State.ACTIVE;
} else {
return BasePort.State.DOWN;
}
}
/**
* process Etcd response for port information.
*
* @param action can be either update or delete
* @param key can contain the id and also target information
* @param value content of the port configuration
*/
@Override
public void processGluonConfig(String action, String key, JsonNode value) {
Collection<BasePort> basePorts;
switch (action) {
case DELETE:
String[] list = key.split(SLASH);
BasePortId basePortId
= BasePortId.portId(list[list.length - 1]);
Set<BasePortId> basePortIds = Sets.newHashSet(basePortId);
removePorts(basePortIds);
break;
case SET:
checkNotNull(value, RESPONSE_NOT_NULL);
basePorts = changeJsonToSub(value);
createPorts(basePorts);
break;
case UPDATE:
checkNotNull(value, RESPONSE_NOT_NULL);
basePorts = changeJsonToSub(value);
updatePorts(basePorts);
break;
default:
log.info("Invalid action is received while processing VPN " +
"port configuration");
}
}
private void parseEtcdResponse(JsonNode jsonNode,
String key,
String action) {
JsonNode modifyValue = null;
if (action.equals(SET)) {
modifyValue = jsonNode.get(key);
}
String[] list = key.split(SLASH);
String target = list[list.length - 2];
if (target.equals(PROTON_BASE_PORT)) {
processGluonConfig(action, key, modifyValue);
}
}
@Override
public void addListener(BasePortListener listener) {
checkNotNull(listener, LISTENER_NOT_NULL);
listeners.add(listener);
}
@Override
public void removeListener(BasePortListener listener) {
checkNotNull(listener, LISTENER_NOT_NULL);
listeners.remove(listener);
}
/**
* Notifies specify event to all listeners.
*
* @param event vpn af config event
*/
private void notifyListeners(BasePortEvent event) {
checkNotNull(event, EVENT_NOT_NULL);
listeners.forEach(listener -> listener.event(event));
}
}