blob: 896b6c655aa281f8bebd0b0e783c1a6c4ddfd100 [file] [log] [blame]
sanghoshinf25d2e02015-11-11 23:07:17 +09001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
sanghoshinf25d2e02015-11-11 23:07:17 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Hyunsun Moon0dba61f2016-03-03 14:05:21 -080016package org.onosproject.openstackinterface.impl;
sanghoshinf25d2e02015-11-11 23:07:17 +090017
18import com.fasterxml.jackson.databind.ObjectMapper;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
sangho93447f12016-02-24 00:33:22 +090021import com.google.common.collect.ImmutableSet;
sanghoshinf25d2e02015-11-11 23:07:17 +090022import com.google.common.collect.Lists;
sangho0c2a3da2016-02-16 13:39:07 +090023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.net.Port;
sangho93447f12016-02-24 00:33:22 +090032import org.onosproject.net.config.ConfigFactory;
33import org.onosproject.net.config.NetworkConfigEvent;
34import org.onosproject.net.config.NetworkConfigListener;
35import org.onosproject.net.config.NetworkConfigRegistry;
Hyunsun Moon0dba61f2016-03-03 14:05:21 -080036import org.onosproject.openstackinterface.OpenstackInterfaceService;
37import org.onosproject.openstackinterface.OpenstackNetwork;
38import org.onosproject.openstackinterface.OpenstackNetworkingConfig;
39import org.onosproject.openstackinterface.OpenstackPort;
40import org.onosproject.openstackinterface.OpenstackRouter;
41import org.onosproject.openstackinterface.OpenstackSecurityGroup;
42import org.onosproject.openstackinterface.OpenstackSubnet;
sangho93447f12016-02-24 00:33:22 +090043import org.onosproject.openstackinterface.web.OpenstackNetworkCodec;
44import org.onosproject.openstackinterface.web.OpenstackPortCodec;
45import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
46import org.onosproject.openstackinterface.web.OpenstackSecurityGroupCodec;
47import org.onosproject.openstackinterface.web.OpenstackSubnetCodec;
sanghoshinf25d2e02015-11-11 23:07:17 +090048import org.slf4j.Logger;
Jian Li9d616492016-03-09 10:52:49 -080049
50import javax.ws.rs.client.Client;
51import javax.ws.rs.client.ClientBuilder;
52import javax.ws.rs.client.Entity;
53import javax.ws.rs.client.Invocation;
54import javax.ws.rs.client.WebTarget;
sanghoshinf25d2e02015-11-11 23:07:17 +090055import javax.ws.rs.core.MediaType;
56import java.io.IOException;
57import java.util.Collection;
Hyunsun Moonf7895202016-01-12 12:21:48 -080058import java.util.Collections;
sanghoshinf25d2e02015-11-11 23:07:17 +090059import java.util.List;
sangho93447f12016-02-24 00:33:22 +090060import java.util.Set;
61import java.util.concurrent.ExecutorService;
62import java.util.concurrent.Executors;
sangho0c2a3da2016-02-16 13:39:07 +090063import java.util.stream.Collectors;
sanghoshinf25d2e02015-11-11 23:07:17 +090064
65import static com.google.common.base.Preconditions.checkNotNull;
66import static com.google.common.net.MediaType.JSON_UTF_8;
sangho93447f12016-02-24 00:33:22 +090067import static org.onlab.util.Tools.groupedThreads;
68import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
sanghoshinf25d2e02015-11-11 23:07:17 +090069import static org.slf4j.LoggerFactory.getLogger;
70
71/**
72 * Handles REST Calls to Openstack Neutron.
73 *
74 */
sangho0c2a3da2016-02-16 13:39:07 +090075@Service
76@Component(immediate = true)
sangho93447f12016-02-24 00:33:22 +090077public class OpenstackInterfaceManager implements OpenstackInterfaceService {
sanghoshinf25d2e02015-11-11 23:07:17 +090078
sangho5db8e052016-01-29 16:08:23 +090079 private static final String URI_NETWORKS = "networks";
80 private static final String URI_PORTS = "ports";
81 private static final String URI_SUBNETS = "subnets";
82 private static final String URI_SECURITY_GROUPS = "security-groups";
83 private static final String URI_TOKENS = "tokens";
84
Daniel Park3a06c522016-01-28 20:51:12 +090085 private static final String PATH_ROUTERS = "routers";
sangho5db8e052016-01-29 16:08:23 +090086 private static final String PATH_NETWORKS = "networks";
87 private static final String PATH_PORTS = "ports";
88 private static final String PATH_SUBNETS = "subnets";
89 private static final String PATH_ACCESS = "access";
90 private static final String PATH_TOKEN = "token";
91 private static final String PATH_ID = "id";
92
93 private static final String HEADER_AUTH_TOKEN = "X-Auth-Token";
94
sanghoshinf25d2e02015-11-11 23:07:17 +090095 private final Logger log = getLogger(getClass());
96 private String neutronUrl;
97 private String keystoneUrl;
98 private String tokenId;
99 private String userName;
100 private String pass;
101
sangho0c2a3da2016-02-16 13:39:07 +0900102 private static final String PORT_NAME = "portName";
103
104 private ApplicationId appId;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected CoreService coreService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho93447f12016-02-24 00:33:22 +0900110 protected NetworkConfigRegistry cfgService;
sangho0c2a3da2016-02-16 13:39:07 +0900111
sangho93447f12016-02-24 00:33:22 +0900112 private InternalConfigListener internalConfigListener = new InternalConfigListener();
113 private ExecutorService networkEventExcutorService =
114 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackinterface", "config-event"));
115
116 private final Set<ConfigFactory> factories = ImmutableSet.of(
117 new ConfigFactory<ApplicationId, OpenstackNetworkingConfig>(APP_SUBJECT_FACTORY,
118 OpenstackNetworkingConfig.class,
119 "openstackinterface") {
120 @Override
121 public OpenstackNetworkingConfig createConfig() {
122 return new OpenstackNetworkingConfig();
123 }
124 }
125 );
126
sangho0c2a3da2016-02-16 13:39:07 +0900127
128 @Activate
129 protected void activate() {
130 appId = coreService
sangho93447f12016-02-24 00:33:22 +0900131 .registerApplication("org.onosproject.openstackinterface");
132
133 factories.forEach(cfgService::registerConfigFactory);
134 cfgService.addListener(internalConfigListener);
sangho0c2a3da2016-02-16 13:39:07 +0900135
136 log.info("started");
137 }
138
139 @Deactivate
140 protected void deactivate() {
sangho93447f12016-02-24 00:33:22 +0900141 cfgService.removeListener(internalConfigListener);
142 factories.forEach(cfgService::unregisterConfigFactory);
sangho0c2a3da2016-02-16 13:39:07 +0900143 log.info("stopped");
sanghoshinf25d2e02015-11-11 23:07:17 +0900144 }
145
146 /**
147 * Returns network information stored in Neutron.
148 *
149 * @return List of OpenstackNetwork
150 */
151 public Collection<OpenstackNetwork> getNetworks() {
152
Jian Li9d616492016-03-09 10:52:49 -0800153 Invocation.Builder builder = getClientBuilder(neutronUrl + URI_NETWORKS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900154 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900155 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900156
Hyunsun Moonf7895202016-01-12 12:21:48 -0800157 log.debug("networks response:" + response);
158
sanghoshinf25d2e02015-11-11 23:07:17 +0900159 ObjectMapper mapper = new ObjectMapper();
160 List<OpenstackNetwork> openstackNetworks = Lists.newArrayList();
161 try {
162 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900163 ArrayNode networkList = (ArrayNode) node.path(PATH_NETWORKS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900164 OpenstackNetworkCodec networkCodec = new OpenstackNetworkCodec();
165 networkList.forEach(n -> openstackNetworks.add(networkCodec.decode((ObjectNode) n, null)));
166 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800167 log.warn("getNetworks()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900168 }
169
Hyunsun Moonf7895202016-01-12 12:21:48 -0800170 openstackNetworks.removeAll(Collections.singleton(null));
sanghoshinf25d2e02015-11-11 23:07:17 +0900171 openstackNetworks.forEach(n -> log.debug("network ID: {}", n.id()));
172
173 return openstackNetworks;
174 }
175
176 /**
177 * Returns port information stored in Neutron.
178 *
179 * @return List of OpenstackPort
180 */
181 public Collection<OpenstackPort> getPorts() {
182
Jian Li9d616492016-03-09 10:52:49 -0800183 Invocation.Builder builder = getClientBuilder(neutronUrl + URI_PORTS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900184 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900185 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900186
187 ObjectMapper mapper = new ObjectMapper();
188 List<OpenstackPort> openstackPorts = Lists.newArrayList();
189 try {
190 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900191 ArrayNode portList = (ArrayNode) node.path(PATH_PORTS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900192 OpenstackPortCodec portCodec = new OpenstackPortCodec();
193 portList.forEach(p -> openstackPorts.add(portCodec.decode((ObjectNode) p, null)));
194 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800195 log.warn("getPorts()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900196 }
197
198 log.debug("port response:" + response);
199 openstackPorts.forEach(n -> log.debug("port ID: {}", n.id()));
200
201 return openstackPorts;
202 }
203
Daniel Park3a06c522016-01-28 20:51:12 +0900204 public Collection<OpenstackRouter> getRouters() {
Jian Li9d616492016-03-09 10:52:49 -0800205 Invocation.Builder builder = getClientBuilder(neutronUrl + PATH_ROUTERS);
Daniel Park3a06c522016-01-28 20:51:12 +0900206 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
207 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
208
209 ObjectMapper mapper = new ObjectMapper();
210 List<OpenstackRouter> openstackRouters = Lists.newArrayList();
211
212 try {
213 ObjectNode node = (ObjectNode) mapper.readTree(response);
214 ArrayNode routerList = (ArrayNode) node.path(PATH_ROUTERS);
215 OpenstackRouterCodec openstackRouterCodec = new OpenstackRouterCodec();
216 routerList.forEach(r -> openstackRouters
217 .add(openstackRouterCodec.decode((ObjectNode) r, null)));
218 } catch (IOException e) {
219 log.warn("getRouters()", e);
220 }
221
222 log.debug("router response:" + response);
223 openstackRouters.forEach(r -> log.debug("router ID: {}", r.id()));
224
225 return openstackRouters;
226 }
227
sanghoshinf25d2e02015-11-11 23:07:17 +0900228 /**
229 * Returns Subnet information in Neutron.
230 *
231 * @return List of OpenstackSubnet
232 */
233 public Collection<OpenstackSubnet> getSubnets() {
Jian Li9d616492016-03-09 10:52:49 -0800234 Invocation.Builder builder = getClientBuilder(neutronUrl + URI_SUBNETS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900235 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900236 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900237
238 ObjectMapper mapper = new ObjectMapper();
239 List<OpenstackSubnet> subnets = Lists.newArrayList();
240 try {
241 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900242 ArrayNode subnetList = (ArrayNode) node.path(PATH_SUBNETS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900243 OpenstackSubnetCodec subnetCodec = new OpenstackSubnetCodec();
244 subnetList.forEach(s -> subnets.add(subnetCodec.decode((ObjectNode) s, null)));
245 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800246 log.warn("getSubnets()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900247 }
248
249 log.debug("subnets response:" + response);
250 subnets.forEach(s -> log.debug("subnet ID: {}", s.id()));
251
252 return subnets;
253 }
254
sangho5db8e052016-01-29 16:08:23 +0900255 /**
256 * Extracts OpenstackSecurityGroup information for the ID.
257 *
258 * @param id Security Group ID
259 * @return OpenstackSecurityGroup object or null if fails
260 */
261 public OpenstackSecurityGroup getSecurityGroup(String id) {
Jian Li9d616492016-03-09 10:52:49 -0800262 Invocation.Builder builder = getClientBuilder(neutronUrl + URI_SECURITY_GROUPS + "/" + id);
sangho5db8e052016-01-29 16:08:23 +0900263 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
264 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
265
266 ObjectMapper mapper = new ObjectMapper();
267 OpenstackSecurityGroup securityGroup = null;
268 try {
269 ObjectNode node = (ObjectNode) mapper.readTree(response);
270 OpenstackSecurityGroupCodec sgCodec = new OpenstackSecurityGroupCodec();
271 securityGroup = sgCodec.decode(node, null);
272 } catch (IOException e) {
273 log.warn("getSecurityGroup()", e);
274 }
275
276 return securityGroup;
277 }
278
Jian Li9d616492016-03-09 10:52:49 -0800279 private Invocation.Builder getClientBuilder(String uri) {
280 Client client = ClientBuilder.newClient();
281 WebTarget wt = client.target(uri);
282 return wt.request(JSON_UTF_8.toString());
sanghoshinf25d2e02015-11-11 23:07:17 +0900283 }
284
285 private String getToken() {
286 if (isTokenInvalid()) {
287 String request = "{\"auth\": {\"tenantName\": \"admin\", " +
288 "\"passwordCredentials\": {\"username\": \"" +
289 userName + "\",\"password\": \"" + pass + "\"}}}";
Jian Li9d616492016-03-09 10:52:49 -0800290 Invocation.Builder builder = getClientBuilder(keystoneUrl + URI_TOKENS);
291 String response = builder.accept(MediaType.APPLICATION_JSON).post(Entity.json(request), String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900292
293 ObjectMapper mapper = new ObjectMapper();
294 try {
295 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900296 tokenId = node.path(PATH_ACCESS).path(PATH_TOKEN).path(PATH_ID).asText();
sanghoshinf25d2e02015-11-11 23:07:17 +0900297 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800298 log.warn("getToken()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900299 }
300 log.debug("token response:" + response);
301 }
302
303 return tokenId;
304 }
305
306 private boolean isTokenInvalid() {
307 //TODO: validation check for the existing token
308 return true;
309 }
310
sangho0c2a3da2016-02-16 13:39:07 +0900311 @Override
312 public Collection<OpenstackPort> ports(String networkId) {
313 return getPorts().stream()
314 .filter(port -> port.networkId().equals(networkId))
315 .collect(Collectors.toList());
316 }
317
318 @Override
319 public Collection<OpenstackPort> ports() {
320 return getPorts();
321 }
322
323 @Override
324 public OpenstackPort port(Port port) {
325 String uuid = port.annotations().value(PORT_NAME).substring(3);
326 return getPorts().stream()
327 .filter(p -> p.id().startsWith(uuid))
328 .findAny().orElse(null);
329 }
330
331 @Override
332 public OpenstackPort port(String portId) {
333 return getPorts().stream()
334 .filter(p -> p.id().equals(portId))
335 .findAny().orElse(null);
336 }
337
338 @Override
339 public OpenstackNetwork network(String networkId) {
Hyunsun Moonb0f09be2016-02-23 04:21:42 -0800340 Collection<OpenstackSubnet> subnets = getSubnets().stream()
341 .filter(s -> s.networkId().equals(networkId))
342 .collect(Collectors.toList());
343
344 OpenstackNetwork openstackNetwork = getNetworks().stream()
sangho0c2a3da2016-02-16 13:39:07 +0900345 .filter(n -> n.id().equals(networkId))
346 .findAny().orElse(null);
Hyunsun Moonb0f09be2016-02-23 04:21:42 -0800347
348 if (openstackNetwork == null) {
349 return null;
350 }
351
352 return OpenstackNetwork.builder()
353 .id(openstackNetwork.id())
354 .name(openstackNetwork.name())
355 .networkType(openstackNetwork.networkType())
356 .segmentId(openstackNetwork.segmentId())
357 .tenantId(openstackNetwork.tenantId())
358 .subnets(subnets)
359 .build();
sangho0c2a3da2016-02-16 13:39:07 +0900360 }
361
362 @Override
363 public Collection<OpenstackNetwork> networks() {
364 return getNetworks();
365 }
366
367 @Override
368 public OpenstackSubnet subnet(String subnetId) {
369 return getSubnets().stream()
370 .filter(subnet -> subnet.id().equals(subnetId))
371 .findAny().orElse(null);
372 }
373
374 @Override
375 public Collection<OpenstackSubnet> subnets() {
376 return getSubnets();
377 }
378
379 @Override
380 public Collection<OpenstackRouter> routers() {
381 return getRouters();
382 }
383
384 @Override
385 public OpenstackRouter router(String routerId) {
386 return getRouters().stream()
387 .filter(router -> router.id().equals(routerId))
388 .findAny().orElse(null);
389 }
390
sangho93447f12016-02-24 00:33:22 +0900391 private class InternalConfigListener implements NetworkConfigListener {
sangho0c2a3da2016-02-16 13:39:07 +0900392
sangho93447f12016-02-24 00:33:22 +0900393 public void configureNetwork() {
394 OpenstackNetworkingConfig cfg =
395 cfgService.getConfig(appId, OpenstackNetworkingConfig.class);
396 if (cfg == null) {
397 log.error("There is no openstack server information in config.");
398 return;
399 }
400
401 neutronUrl = checkNotNull(cfg.neutronServer());
402 keystoneUrl = checkNotNull(cfg.keystoneServer());
403 userName = checkNotNull(cfg.userName());
404 pass = checkNotNull(cfg.password());
405 }
406
407 @Override
408 public void event(NetworkConfigEvent event) {
409 if (((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
410 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) &&
411 event.configClass().equals(OpenstackNetworkingConfig.class)) {
412
413 log.info("Network configuration changed");
414 networkEventExcutorService.execute(this::configureNetwork);
415 }
416 }
sangho0c2a3da2016-02-16 13:39:07 +0900417 }
Jian Li9d616492016-03-09 10:52:49 -0800418}