blob: 896b6c655aa281f8bebd0b0e783c1a6c4ddfd100 [file] [log] [blame]
/*
* Copyright 2016-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.openstackinterface.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
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.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.Port;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.openstackinterface.OpenstackInterfaceService;
import org.onosproject.openstackinterface.OpenstackNetwork;
import org.onosproject.openstackinterface.OpenstackNetworkingConfig;
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstackinterface.OpenstackRouter;
import org.onosproject.openstackinterface.OpenstackSecurityGroup;
import org.onosproject.openstackinterface.OpenstackSubnet;
import org.onosproject.openstackinterface.web.OpenstackNetworkCodec;
import org.onosproject.openstackinterface.web.OpenstackPortCodec;
import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
import org.onosproject.openstackinterface.web.OpenstackSecurityGroupCodec;
import org.onosproject.openstackinterface.web.OpenstackSubnetCodec;
import org.slf4j.Logger;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Handles REST Calls to Openstack Neutron.
*
*/
@Service
@Component(immediate = true)
public class OpenstackInterfaceManager implements OpenstackInterfaceService {
private static final String URI_NETWORKS = "networks";
private static final String URI_PORTS = "ports";
private static final String URI_SUBNETS = "subnets";
private static final String URI_SECURITY_GROUPS = "security-groups";
private static final String URI_TOKENS = "tokens";
private static final String PATH_ROUTERS = "routers";
private static final String PATH_NETWORKS = "networks";
private static final String PATH_PORTS = "ports";
private static final String PATH_SUBNETS = "subnets";
private static final String PATH_ACCESS = "access";
private static final String PATH_TOKEN = "token";
private static final String PATH_ID = "id";
private static final String HEADER_AUTH_TOKEN = "X-Auth-Token";
private final Logger log = getLogger(getClass());
private String neutronUrl;
private String keystoneUrl;
private String tokenId;
private String userName;
private String pass;
private static final String PORT_NAME = "portName";
private ApplicationId appId;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry cfgService;
private InternalConfigListener internalConfigListener = new InternalConfigListener();
private ExecutorService networkEventExcutorService =
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackinterface", "config-event"));
private final Set<ConfigFactory> factories = ImmutableSet.of(
new ConfigFactory<ApplicationId, OpenstackNetworkingConfig>(APP_SUBJECT_FACTORY,
OpenstackNetworkingConfig.class,
"openstackinterface") {
@Override
public OpenstackNetworkingConfig createConfig() {
return new OpenstackNetworkingConfig();
}
}
);
@Activate
protected void activate() {
appId = coreService
.registerApplication("org.onosproject.openstackinterface");
factories.forEach(cfgService::registerConfigFactory);
cfgService.addListener(internalConfigListener);
log.info("started");
}
@Deactivate
protected void deactivate() {
cfgService.removeListener(internalConfigListener);
factories.forEach(cfgService::unregisterConfigFactory);
log.info("stopped");
}
/**
* Returns network information stored in Neutron.
*
* @return List of OpenstackNetwork
*/
public Collection<OpenstackNetwork> getNetworks() {
Invocation.Builder builder = getClientBuilder(neutronUrl + URI_NETWORKS);
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
log.debug("networks response:" + response);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackNetwork> openstackNetworks = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode networkList = (ArrayNode) node.path(PATH_NETWORKS);
OpenstackNetworkCodec networkCodec = new OpenstackNetworkCodec();
networkList.forEach(n -> openstackNetworks.add(networkCodec.decode((ObjectNode) n, null)));
} catch (IOException e) {
log.warn("getNetworks()", e);
}
openstackNetworks.removeAll(Collections.singleton(null));
openstackNetworks.forEach(n -> log.debug("network ID: {}", n.id()));
return openstackNetworks;
}
/**
* Returns port information stored in Neutron.
*
* @return List of OpenstackPort
*/
public Collection<OpenstackPort> getPorts() {
Invocation.Builder builder = getClientBuilder(neutronUrl + URI_PORTS);
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackPort> openstackPorts = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode portList = (ArrayNode) node.path(PATH_PORTS);
OpenstackPortCodec portCodec = new OpenstackPortCodec();
portList.forEach(p -> openstackPorts.add(portCodec.decode((ObjectNode) p, null)));
} catch (IOException e) {
log.warn("getPorts()", e);
}
log.debug("port response:" + response);
openstackPorts.forEach(n -> log.debug("port ID: {}", n.id()));
return openstackPorts;
}
public Collection<OpenstackRouter> getRouters() {
Invocation.Builder builder = getClientBuilder(neutronUrl + PATH_ROUTERS);
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackRouter> openstackRouters = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode routerList = (ArrayNode) node.path(PATH_ROUTERS);
OpenstackRouterCodec openstackRouterCodec = new OpenstackRouterCodec();
routerList.forEach(r -> openstackRouters
.add(openstackRouterCodec.decode((ObjectNode) r, null)));
} catch (IOException e) {
log.warn("getRouters()", e);
}
log.debug("router response:" + response);
openstackRouters.forEach(r -> log.debug("router ID: {}", r.id()));
return openstackRouters;
}
/**
* Returns Subnet information in Neutron.
*
* @return List of OpenstackSubnet
*/
public Collection<OpenstackSubnet> getSubnets() {
Invocation.Builder builder = getClientBuilder(neutronUrl + URI_SUBNETS);
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
List<OpenstackSubnet> subnets = Lists.newArrayList();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
ArrayNode subnetList = (ArrayNode) node.path(PATH_SUBNETS);
OpenstackSubnetCodec subnetCodec = new OpenstackSubnetCodec();
subnetList.forEach(s -> subnets.add(subnetCodec.decode((ObjectNode) s, null)));
} catch (IOException e) {
log.warn("getSubnets()", e);
}
log.debug("subnets response:" + response);
subnets.forEach(s -> log.debug("subnet ID: {}", s.id()));
return subnets;
}
/**
* Extracts OpenstackSecurityGroup information for the ID.
*
* @param id Security Group ID
* @return OpenstackSecurityGroup object or null if fails
*/
public OpenstackSecurityGroup getSecurityGroup(String id) {
Invocation.Builder builder = getClientBuilder(neutronUrl + URI_SECURITY_GROUPS + "/" + id);
String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
ObjectMapper mapper = new ObjectMapper();
OpenstackSecurityGroup securityGroup = null;
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
OpenstackSecurityGroupCodec sgCodec = new OpenstackSecurityGroupCodec();
securityGroup = sgCodec.decode(node, null);
} catch (IOException e) {
log.warn("getSecurityGroup()", e);
}
return securityGroup;
}
private Invocation.Builder getClientBuilder(String uri) {
Client client = ClientBuilder.newClient();
WebTarget wt = client.target(uri);
return wt.request(JSON_UTF_8.toString());
}
private String getToken() {
if (isTokenInvalid()) {
String request = "{\"auth\": {\"tenantName\": \"admin\", " +
"\"passwordCredentials\": {\"username\": \"" +
userName + "\",\"password\": \"" + pass + "\"}}}";
Invocation.Builder builder = getClientBuilder(keystoneUrl + URI_TOKENS);
String response = builder.accept(MediaType.APPLICATION_JSON).post(Entity.json(request), String.class);
ObjectMapper mapper = new ObjectMapper();
try {
ObjectNode node = (ObjectNode) mapper.readTree(response);
tokenId = node.path(PATH_ACCESS).path(PATH_TOKEN).path(PATH_ID).asText();
} catch (IOException e) {
log.warn("getToken()", e);
}
log.debug("token response:" + response);
}
return tokenId;
}
private boolean isTokenInvalid() {
//TODO: validation check for the existing token
return true;
}
@Override
public Collection<OpenstackPort> ports(String networkId) {
return getPorts().stream()
.filter(port -> port.networkId().equals(networkId))
.collect(Collectors.toList());
}
@Override
public Collection<OpenstackPort> ports() {
return getPorts();
}
@Override
public OpenstackPort port(Port port) {
String uuid = port.annotations().value(PORT_NAME).substring(3);
return getPorts().stream()
.filter(p -> p.id().startsWith(uuid))
.findAny().orElse(null);
}
@Override
public OpenstackPort port(String portId) {
return getPorts().stream()
.filter(p -> p.id().equals(portId))
.findAny().orElse(null);
}
@Override
public OpenstackNetwork network(String networkId) {
Collection<OpenstackSubnet> subnets = getSubnets().stream()
.filter(s -> s.networkId().equals(networkId))
.collect(Collectors.toList());
OpenstackNetwork openstackNetwork = getNetworks().stream()
.filter(n -> n.id().equals(networkId))
.findAny().orElse(null);
if (openstackNetwork == null) {
return null;
}
return OpenstackNetwork.builder()
.id(openstackNetwork.id())
.name(openstackNetwork.name())
.networkType(openstackNetwork.networkType())
.segmentId(openstackNetwork.segmentId())
.tenantId(openstackNetwork.tenantId())
.subnets(subnets)
.build();
}
@Override
public Collection<OpenstackNetwork> networks() {
return getNetworks();
}
@Override
public OpenstackSubnet subnet(String subnetId) {
return getSubnets().stream()
.filter(subnet -> subnet.id().equals(subnetId))
.findAny().orElse(null);
}
@Override
public Collection<OpenstackSubnet> subnets() {
return getSubnets();
}
@Override
public Collection<OpenstackRouter> routers() {
return getRouters();
}
@Override
public OpenstackRouter router(String routerId) {
return getRouters().stream()
.filter(router -> router.id().equals(routerId))
.findAny().orElse(null);
}
private class InternalConfigListener implements NetworkConfigListener {
public void configureNetwork() {
OpenstackNetworkingConfig cfg =
cfgService.getConfig(appId, OpenstackNetworkingConfig.class);
if (cfg == null) {
log.error("There is no openstack server information in config.");
return;
}
neutronUrl = checkNotNull(cfg.neutronServer());
keystoneUrl = checkNotNull(cfg.keystoneServer());
userName = checkNotNull(cfg.userName());
pass = checkNotNull(cfg.password());
}
@Override
public void event(NetworkConfigEvent event) {
if (((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) &&
event.configClass().equals(OpenstackNetworkingConfig.class)) {
log.info("Network configuration changed");
networkEventExcutorService.execute(this::configureNetwork);
}
}
}
}