blob: 3fccce309e2f00d03b945359ef74ee7e4268b921 [file] [log] [blame]
sanghoshinf25d2e02015-11-11 23:07:17 +09001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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;
23import com.sun.jersey.api.client.Client;
24import com.sun.jersey.api.client.WebResource;
sangho0c2a3da2016-02-16 13:39:07 +090025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.apache.felix.scr.annotations.Service;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.net.Port;
sangho93447f12016-02-24 00:33:22 +090034import org.onosproject.net.config.ConfigFactory;
35import org.onosproject.net.config.NetworkConfigEvent;
36import org.onosproject.net.config.NetworkConfigListener;
37import org.onosproject.net.config.NetworkConfigRegistry;
Hyunsun Moon0dba61f2016-03-03 14:05:21 -080038import org.onosproject.openstackinterface.OpenstackInterfaceService;
39import org.onosproject.openstackinterface.OpenstackNetwork;
40import org.onosproject.openstackinterface.OpenstackNetworkingConfig;
41import org.onosproject.openstackinterface.OpenstackPort;
42import org.onosproject.openstackinterface.OpenstackRouter;
43import org.onosproject.openstackinterface.OpenstackSecurityGroup;
44import org.onosproject.openstackinterface.OpenstackSubnet;
sangho93447f12016-02-24 00:33:22 +090045import org.onosproject.openstackinterface.web.OpenstackNetworkCodec;
46import org.onosproject.openstackinterface.web.OpenstackPortCodec;
47import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
48import org.onosproject.openstackinterface.web.OpenstackSecurityGroupCodec;
49import org.onosproject.openstackinterface.web.OpenstackSubnetCodec;
sanghoshinf25d2e02015-11-11 23:07:17 +090050import org.slf4j.Logger;
51import javax.ws.rs.core.MediaType;
52import java.io.IOException;
53import java.util.Collection;
Hyunsun Moonf7895202016-01-12 12:21:48 -080054import java.util.Collections;
sanghoshinf25d2e02015-11-11 23:07:17 +090055import java.util.List;
sangho93447f12016-02-24 00:33:22 +090056import java.util.Set;
57import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
sangho0c2a3da2016-02-16 13:39:07 +090059import java.util.stream.Collectors;
sanghoshinf25d2e02015-11-11 23:07:17 +090060
61import static com.google.common.base.Preconditions.checkNotNull;
62import static com.google.common.net.MediaType.JSON_UTF_8;
sangho93447f12016-02-24 00:33:22 +090063import static org.onlab.util.Tools.groupedThreads;
64import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
sanghoshinf25d2e02015-11-11 23:07:17 +090065import static org.slf4j.LoggerFactory.getLogger;
66
67/**
68 * Handles REST Calls to Openstack Neutron.
69 *
70 */
sangho0c2a3da2016-02-16 13:39:07 +090071@Service
72@Component(immediate = true)
sangho93447f12016-02-24 00:33:22 +090073public class OpenstackInterfaceManager implements OpenstackInterfaceService {
sanghoshinf25d2e02015-11-11 23:07:17 +090074
sangho5db8e052016-01-29 16:08:23 +090075 private static final String URI_NETWORKS = "networks";
76 private static final String URI_PORTS = "ports";
77 private static final String URI_SUBNETS = "subnets";
78 private static final String URI_SECURITY_GROUPS = "security-groups";
79 private static final String URI_TOKENS = "tokens";
80
Daniel Park3a06c522016-01-28 20:51:12 +090081 private static final String PATH_ROUTERS = "routers";
sangho5db8e052016-01-29 16:08:23 +090082 private static final String PATH_NETWORKS = "networks";
83 private static final String PATH_PORTS = "ports";
84 private static final String PATH_SUBNETS = "subnets";
85 private static final String PATH_ACCESS = "access";
86 private static final String PATH_TOKEN = "token";
87 private static final String PATH_ID = "id";
88
89 private static final String HEADER_AUTH_TOKEN = "X-Auth-Token";
90
sanghoshinf25d2e02015-11-11 23:07:17 +090091 private final Logger log = getLogger(getClass());
92 private String neutronUrl;
93 private String keystoneUrl;
94 private String tokenId;
95 private String userName;
96 private String pass;
97
sangho0c2a3da2016-02-16 13:39:07 +090098 private static final String PORT_NAME = "portName";
99
100 private ApplicationId appId;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected CoreService coreService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho93447f12016-02-24 00:33:22 +0900106 protected NetworkConfigRegistry cfgService;
sangho0c2a3da2016-02-16 13:39:07 +0900107
sangho93447f12016-02-24 00:33:22 +0900108 private InternalConfigListener internalConfigListener = new InternalConfigListener();
109 private ExecutorService networkEventExcutorService =
110 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackinterface", "config-event"));
111
112 private final Set<ConfigFactory> factories = ImmutableSet.of(
113 new ConfigFactory<ApplicationId, OpenstackNetworkingConfig>(APP_SUBJECT_FACTORY,
114 OpenstackNetworkingConfig.class,
115 "openstackinterface") {
116 @Override
117 public OpenstackNetworkingConfig createConfig() {
118 return new OpenstackNetworkingConfig();
119 }
120 }
121 );
122
sangho0c2a3da2016-02-16 13:39:07 +0900123
124 @Activate
125 protected void activate() {
126 appId = coreService
sangho93447f12016-02-24 00:33:22 +0900127 .registerApplication("org.onosproject.openstackinterface");
128
129 factories.forEach(cfgService::registerConfigFactory);
130 cfgService.addListener(internalConfigListener);
sangho0c2a3da2016-02-16 13:39:07 +0900131
132 log.info("started");
133 }
134
135 @Deactivate
136 protected void deactivate() {
sangho93447f12016-02-24 00:33:22 +0900137 cfgService.removeListener(internalConfigListener);
138 factories.forEach(cfgService::unregisterConfigFactory);
sangho0c2a3da2016-02-16 13:39:07 +0900139 log.info("stopped");
sanghoshinf25d2e02015-11-11 23:07:17 +0900140 }
141
142 /**
143 * Returns network information stored in Neutron.
144 *
145 * @return List of OpenstackNetwork
146 */
147 public Collection<OpenstackNetwork> getNetworks() {
148
sangho5db8e052016-01-29 16:08:23 +0900149 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_NETWORKS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900150 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900151 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900152
Hyunsun Moonf7895202016-01-12 12:21:48 -0800153 log.debug("networks response:" + response);
154
sanghoshinf25d2e02015-11-11 23:07:17 +0900155 ObjectMapper mapper = new ObjectMapper();
156 List<OpenstackNetwork> openstackNetworks = Lists.newArrayList();
157 try {
158 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900159 ArrayNode networkList = (ArrayNode) node.path(PATH_NETWORKS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900160 OpenstackNetworkCodec networkCodec = new OpenstackNetworkCodec();
161 networkList.forEach(n -> openstackNetworks.add(networkCodec.decode((ObjectNode) n, null)));
162 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800163 log.warn("getNetworks()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900164 }
165
Hyunsun Moonf7895202016-01-12 12:21:48 -0800166 openstackNetworks.removeAll(Collections.singleton(null));
sanghoshinf25d2e02015-11-11 23:07:17 +0900167 openstackNetworks.forEach(n -> log.debug("network ID: {}", n.id()));
168
169 return openstackNetworks;
170 }
171
172 /**
173 * Returns port information stored in Neutron.
174 *
175 * @return List of OpenstackPort
176 */
177 public Collection<OpenstackPort> getPorts() {
178
sangho5db8e052016-01-29 16:08:23 +0900179 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_PORTS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900180 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900181 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900182
183 ObjectMapper mapper = new ObjectMapper();
184 List<OpenstackPort> openstackPorts = Lists.newArrayList();
185 try {
186 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900187 ArrayNode portList = (ArrayNode) node.path(PATH_PORTS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900188 OpenstackPortCodec portCodec = new OpenstackPortCodec();
189 portList.forEach(p -> openstackPorts.add(portCodec.decode((ObjectNode) p, null)));
190 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800191 log.warn("getPorts()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900192 }
193
194 log.debug("port response:" + response);
195 openstackPorts.forEach(n -> log.debug("port ID: {}", n.id()));
196
197 return openstackPorts;
198 }
199
Daniel Park3a06c522016-01-28 20:51:12 +0900200 public Collection<OpenstackRouter> getRouters() {
201 WebResource.Builder builder = getClientBuilder(neutronUrl + PATH_ROUTERS);
202 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
203 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
204
205 ObjectMapper mapper = new ObjectMapper();
206 List<OpenstackRouter> openstackRouters = Lists.newArrayList();
207
208 try {
209 ObjectNode node = (ObjectNode) mapper.readTree(response);
210 ArrayNode routerList = (ArrayNode) node.path(PATH_ROUTERS);
211 OpenstackRouterCodec openstackRouterCodec = new OpenstackRouterCodec();
212 routerList.forEach(r -> openstackRouters
213 .add(openstackRouterCodec.decode((ObjectNode) r, null)));
214 } catch (IOException e) {
215 log.warn("getRouters()", e);
216 }
217
218 log.debug("router response:" + response);
219 openstackRouters.forEach(r -> log.debug("router ID: {}", r.id()));
220
221 return openstackRouters;
222 }
223
sanghoshinf25d2e02015-11-11 23:07:17 +0900224 /**
225 * Returns Subnet information in Neutron.
226 *
227 * @return List of OpenstackSubnet
228 */
229 public Collection<OpenstackSubnet> getSubnets() {
230
sangho5db8e052016-01-29 16:08:23 +0900231 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_SUBNETS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900232 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900233 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900234
235 ObjectMapper mapper = new ObjectMapper();
236 List<OpenstackSubnet> subnets = Lists.newArrayList();
237 try {
238 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900239 ArrayNode subnetList = (ArrayNode) node.path(PATH_SUBNETS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900240 OpenstackSubnetCodec subnetCodec = new OpenstackSubnetCodec();
241 subnetList.forEach(s -> subnets.add(subnetCodec.decode((ObjectNode) s, null)));
242 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800243 log.warn("getSubnets()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900244 }
245
246 log.debug("subnets response:" + response);
247 subnets.forEach(s -> log.debug("subnet ID: {}", s.id()));
248
249 return subnets;
250 }
251
sangho5db8e052016-01-29 16:08:23 +0900252 /**
253 * Extracts OpenstackSecurityGroup information for the ID.
254 *
255 * @param id Security Group ID
256 * @return OpenstackSecurityGroup object or null if fails
257 */
258 public OpenstackSecurityGroup getSecurityGroup(String id) {
259 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_SECURITY_GROUPS + "/" + id);
260 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
261 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
262
263 ObjectMapper mapper = new ObjectMapper();
264 OpenstackSecurityGroup securityGroup = null;
265 try {
266 ObjectNode node = (ObjectNode) mapper.readTree(response);
267 OpenstackSecurityGroupCodec sgCodec = new OpenstackSecurityGroupCodec();
268 securityGroup = sgCodec.decode(node, null);
269 } catch (IOException e) {
270 log.warn("getSecurityGroup()", e);
271 }
272
273 return securityGroup;
274 }
275
sanghoshinf25d2e02015-11-11 23:07:17 +0900276 private WebResource.Builder getClientBuilder(String uri) {
277 Client client = Client.create();
278 WebResource resource = client.resource(uri);
279 return resource.accept(JSON_UTF_8.toString())
280 .type(JSON_UTF_8.toString());
281 }
282
283 private String getToken() {
284 if (isTokenInvalid()) {
285 String request = "{\"auth\": {\"tenantName\": \"admin\", " +
286 "\"passwordCredentials\": {\"username\": \"" +
287 userName + "\",\"password\": \"" + pass + "\"}}}";
sangho5db8e052016-01-29 16:08:23 +0900288 WebResource.Builder builder = getClientBuilder(keystoneUrl + URI_TOKENS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900289 String response = builder.accept(MediaType.APPLICATION_JSON).post(String.class, request);
290
291 ObjectMapper mapper = new ObjectMapper();
292 try {
293 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900294 tokenId = node.path(PATH_ACCESS).path(PATH_TOKEN).path(PATH_ID).asText();
sanghoshinf25d2e02015-11-11 23:07:17 +0900295 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800296 log.warn("getToken()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900297 }
298 log.debug("token response:" + response);
299 }
300
301 return tokenId;
302 }
303
304 private boolean isTokenInvalid() {
305 //TODO: validation check for the existing token
306 return true;
307 }
308
sangho0c2a3da2016-02-16 13:39:07 +0900309 @Override
310 public Collection<OpenstackPort> ports(String networkId) {
311 return getPorts().stream()
312 .filter(port -> port.networkId().equals(networkId))
313 .collect(Collectors.toList());
314 }
315
316 @Override
317 public Collection<OpenstackPort> ports() {
318 return getPorts();
319 }
320
321 @Override
322 public OpenstackPort port(Port port) {
323 String uuid = port.annotations().value(PORT_NAME).substring(3);
324 return getPorts().stream()
325 .filter(p -> p.id().startsWith(uuid))
326 .findAny().orElse(null);
327 }
328
329 @Override
330 public OpenstackPort port(String portId) {
331 return getPorts().stream()
332 .filter(p -> p.id().equals(portId))
333 .findAny().orElse(null);
334 }
335
336 @Override
337 public OpenstackNetwork network(String networkId) {
Hyunsun Moonb0f09be2016-02-23 04:21:42 -0800338 Collection<OpenstackSubnet> subnets = getSubnets().stream()
339 .filter(s -> s.networkId().equals(networkId))
340 .collect(Collectors.toList());
341
342 OpenstackNetwork openstackNetwork = getNetworks().stream()
sangho0c2a3da2016-02-16 13:39:07 +0900343 .filter(n -> n.id().equals(networkId))
344 .findAny().orElse(null);
Hyunsun Moonb0f09be2016-02-23 04:21:42 -0800345
346 if (openstackNetwork == null) {
347 return null;
348 }
349
350 return OpenstackNetwork.builder()
351 .id(openstackNetwork.id())
352 .name(openstackNetwork.name())
353 .networkType(openstackNetwork.networkType())
354 .segmentId(openstackNetwork.segmentId())
355 .tenantId(openstackNetwork.tenantId())
356 .subnets(subnets)
357 .build();
sangho0c2a3da2016-02-16 13:39:07 +0900358 }
359
360 @Override
361 public Collection<OpenstackNetwork> networks() {
362 return getNetworks();
363 }
364
365 @Override
366 public OpenstackSubnet subnet(String subnetId) {
367 return getSubnets().stream()
368 .filter(subnet -> subnet.id().equals(subnetId))
369 .findAny().orElse(null);
370 }
371
372 @Override
373 public Collection<OpenstackSubnet> subnets() {
374 return getSubnets();
375 }
376
377 @Override
378 public Collection<OpenstackRouter> routers() {
379 return getRouters();
380 }
381
382 @Override
383 public OpenstackRouter router(String routerId) {
384 return getRouters().stream()
385 .filter(router -> router.id().equals(routerId))
386 .findAny().orElse(null);
387 }
388
sangho93447f12016-02-24 00:33:22 +0900389 private class InternalConfigListener implements NetworkConfigListener {
sangho0c2a3da2016-02-16 13:39:07 +0900390
sangho93447f12016-02-24 00:33:22 +0900391 public void configureNetwork() {
392 OpenstackNetworkingConfig cfg =
393 cfgService.getConfig(appId, OpenstackNetworkingConfig.class);
394 if (cfg == null) {
395 log.error("There is no openstack server information in config.");
396 return;
397 }
398
399 neutronUrl = checkNotNull(cfg.neutronServer());
400 keystoneUrl = checkNotNull(cfg.keystoneServer());
401 userName = checkNotNull(cfg.userName());
402 pass = checkNotNull(cfg.password());
403 }
404
405 @Override
406 public void event(NetworkConfigEvent event) {
407 if (((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
408 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) &&
409 event.configClass().equals(OpenstackNetworkingConfig.class)) {
410
411 log.info("Network configuration changed");
412 networkEventExcutorService.execute(this::configureNetwork);
413 }
414 }
sangho0c2a3da2016-02-16 13:39:07 +0900415 }
416}